"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateFiles = exports.deleteFiles = exports.removeEmptyDirs = exports.findFiles = exports.updateFile = exports.removeBlocks = exports.replaceNextLine = exports.removeNextLine = exports.removeCurrentLine = exports.markupRegex = exports.markupComment = exports.MarkupComments = void 0;
var gluegun_1 = require("gluegun");
var pathlib = require("path");
// all possible comment types
var MarkupComments;
(function (MarkupComments) {
    MarkupComments["RemoveCurrentLine"] = "remove-current-line";
    MarkupComments["RemoveNextLine"] = "remove-next-line";
    MarkupComments["RemoveBlockStart"] = "remove-block-start";
    MarkupComments["RemoveBlockEnd"] = "remove-block-end";
    MarkupComments["RemoveFile"] = "remove-file";
    MarkupComments["ReplaceNextLine"] = "replace-next-line";
})(MarkupComments || (exports.MarkupComments = MarkupComments = {}));
// markup comments follow format:
// // @prefix ActionName
var markupComment = function (prefix, commentType) {
    return "".concat(prefix, " ").concat(commentType);
};
exports.markupComment = markupComment;
var markupRegex = function (prefix) {
    var pattern = "(\\/\\/|#)\\s*".concat(prefix, ".*|{?\\/.*").concat(prefix, ".*\\/}?");
    return new RegExp(pattern, "gm");
};
exports.markupRegex = markupRegex;
/**
 * Take the file content as a string and remove any
 * line of code with an `// @x remove-current-line` comment
 */
function removeCurrentLine(contents, comment) {
    var lines = contents.split("\n");
    var result = lines.filter(function (line) { return !line.includes(comment); });
    return result.join("\n");
}
exports.removeCurrentLine = removeCurrentLine;
/**
 * Take the file content as a string and remove the next line
 * of code with the REMOVE_NEXT_LINE comment before it
 */
function removeNextLine(contents, comment) {
    var lines = contents.split("\n");
    var result = lines.filter(function (line, index) {
        var prevLine = lines[index - 1];
        var preserveCurrent = line.includes(comment) === false;
        var preservePrevious = prevLine !== undefined && prevLine.includes(comment) === false;
        if (index === 0) {
            // if we are on the first line, there is no previous line to check
            return preserveCurrent;
        }
        // keep current line if there is no comment in current or previous line
        var keepLine = preserveCurrent && preservePrevious;
        return keepLine;
    });
    return result.join("\n");
}
exports.removeNextLine = removeNextLine;
/**
 * Take the file content as a string and replace the current line
 * of code with the contents of the REPLACE_NEXT_LINE comment before it
 * example: // @prefix replace-next-line const newLine = "new line"
 */
function replaceNextLine(contents, comment) {
    var lines = contents.split("\n");
    var result = lines.map(function (line, index) {
        var prevLine = lines[index - 1];
        if (prevLine === null || prevLine === void 0 ? void 0 : prevLine.includes(comment)) {
            var newLineContent = prevLine.replace("//", "").replace(comment, "").trim();
            return newLineContent;
        }
        else {
            return line;
        }
    });
    return result.join("\n");
}
exports.replaceNextLine = replaceNextLine;
/**
 * Take the file content as a string and remove the lines of code between
 * start and end block comments
 */
function removeBlocks(contents, comment) {
    var start = comment.start, end = comment.end;
    var lines = contents.split("\n");
    var findIndex = function (l, c) {
        return l.findIndex(function (line) { return line.includes(c); });
    };
    var NOT_FOUND = -1;
    var blockStartIndex = findIndex(lines, start);
    var blockEndIndex = findIndex(lines, end);
    var blockExists = findIndex(lines, start) !== NOT_FOUND && blockEndIndex !== NOT_FOUND;
    if (blockExists) {
        var blockLength = blockEndIndex - blockStartIndex + 1;
        lines.splice(blockStartIndex, blockLength); // mutates `lines`
    }
    var updateContents = lines.join("\n");
    var anotherBlockExists = findIndex(lines, start) !== NOT_FOUND && findIndex(lines, end) !== NOT_FOUND;
    if (anotherBlockExists) {
        return removeBlocks(updateContents, comment);
    }
    return updateContents;
}
exports.removeBlocks = removeBlocks;
/**
 * Perform all operations possible in a file
 * @param contents The file contents as a string
 * @return The file contents with all remove operations performed
 */
function updateFile(contents, markupPrefix) {
    var result = contents;
    result = removeBlocks(result, {
        start: (0, exports.markupComment)(markupPrefix, MarkupComments.RemoveBlockStart),
        end: (0, exports.markupComment)(markupPrefix, MarkupComments.RemoveBlockEnd),
    });
    result = removeCurrentLine(result, (0, exports.markupComment)(markupPrefix, MarkupComments.RemoveCurrentLine));
    result = removeNextLine(result, (0, exports.markupComment)(markupPrefix, MarkupComments.RemoveNextLine));
    result = replaceNextLine(result, (0, exports.markupComment)(markupPrefix, MarkupComments.ReplaceNextLine));
    return result;
}
exports.updateFile = updateFile;
var DEFAULT_MATCHING_GLOBS = [
    "!**/.DS_Store",
    "!**/.expo{,/**}",
    "!**/.git{,/**}",
    "!**/.vscode{,/**}",
    "!**/node_modules{,/**}",
    "!**/ios/build{,/**}",
    "!**/ios/Pods{,/**}",
    "!**/ios/*.xcworkspace{,/**}",
    "!**/android/build{,/**}",
    "!**/android/app/build{,/**}",
];
/**
 * Perform replace on all types of markup
 * @param contents The file contents as a string
 * @return The file contents with all CommentType removed
 */
function findFiles(targetDir, matching) {
    var filePaths = gluegun_1.filesystem
        .cwd(targetDir)
        .find({
        matching: matching !== null && matching !== void 0 ? matching : DEFAULT_MATCHING_GLOBS,
        recursive: true,
        files: true,
        directories: false,
    })
        .map(function (path) { return pathlib.join(targetDir, path); });
    return filePaths;
}
exports.findFiles = findFiles;
function removeEmptyDirs(_a) {
    var targetDir = _a.targetDir, dryRun = _a.dryRun, _b = _a.matching, matching = _b === void 0 ? DEFAULT_MATCHING_GLOBS : _b;
    var removedDirs = [];
    var getEmptyDirPaths = function () {
        return gluegun_1.filesystem
            .cwd(targetDir)
            .find({
            matching: matching,
            recursive: true,
            files: false,
            directories: true,
        })
            .map(function (path) { return pathlib.join(targetDir, path); })
            .filter(function (path) { var _a; return !((_a = gluegun_1.filesystem.list(path)) === null || _a === void 0 ? void 0 : _a.length); });
    };
    var emptyDirPaths = getEmptyDirPaths();
    while (emptyDirPaths.length > 0) {
        emptyDirPaths.forEach(function (path) {
            if (!dryRun)
                gluegun_1.filesystem.remove(path);
            removedDirs.push(path);
        });
        emptyDirPaths = getEmptyDirPaths();
    }
    return removedDirs;
}
exports.removeEmptyDirs = removeEmptyDirs;
function deleteFiles(_a) {
    var filePaths = _a.filePaths, comment = _a.comment, _b = _a.dryRun, dryRun = _b === void 0 ? true : _b;
    return __awaiter(this, void 0, void 0, function () {
        var commentResults;
        var _this = this;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0: return [4 /*yield*/, Promise.allSettled(filePaths.map(function (path) { return __awaiter(_this, void 0, void 0, function () {
                        var remove, exists;
                        return __generator(this, function (_a) {
                            switch (_a.label) {
                                case 0:
                                    remove = gluegun_1.filesystem.remove;
                                    exists = gluegun_1.patching.exists;
                                    return [4 /*yield*/, exists(path, comment)];
                                case 1:
                                    if (_a.sent()) {
                                        if (!dryRun) {
                                            remove(path);
                                        }
                                        return [2 /*return*/, { path: path }];
                                    }
                                    return [2 /*return*/];
                            }
                        });
                    }); }))];
                case 1:
                    commentResults = _c.sent();
                    return [2 /*return*/, commentResults];
            }
        });
    });
}
exports.deleteFiles = deleteFiles;
function updateFiles(_a) {
    var filePaths = _a.filePaths, markupPrefix = _a.markupPrefix, _b = _a.dryRun, dryRun = _b === void 0 ? true : _b, _c = _a.removeMarkupOnly, removeMarkupOnly = _c === void 0 ? false : _c;
    return __awaiter(this, void 0, void 0, function () {
        var sanitize, commentResults;
        var _this = this;
        return __generator(this, function (_d) {
            switch (_d.label) {
                case 0:
                    sanitize = function (contents) {
                        return contents.replace((0, exports.markupRegex)(markupPrefix), "");
                    };
                    return [4 /*yield*/, Promise.allSettled(filePaths.map(function (path) { return __awaiter(_this, void 0, void 0, function () {
                            var exists, update, read, comments, contents, sanitized, operationComments, shouldUpdate, before_1;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        exists = gluegun_1.patching.exists, update = gluegun_1.patching.update;
                                        read = gluegun_1.filesystem.read;
                                        comments = [];
                                        return [4 /*yield*/, exists(path, (0, exports.markupComment)(markupPrefix, MarkupComments.RemoveFile))];
                                    case 1:
                                        // remove files first
                                        if (_a.sent()) {
                                            console.log("removing file", path);
                                            if (!dryRun) {
                                                if (removeMarkupOnly) {
                                                    contents = read(path);
                                                    sanitized = sanitize(contents);
                                                    gluegun_1.filesystem.write(path, sanitized);
                                                }
                                                else {
                                                    gluegun_1.filesystem.remove(path);
                                                }
                                            }
                                            comments.push(MarkupComments.RemoveFile);
                                            return [2 /*return*/, { path: path, comments: comments }];
                                        }
                                        operationComments = Object.keys(MarkupComments)
                                            .filter(function (key) { return MarkupComments[key] !== MarkupComments.RemoveFile; })
                                            .map(function (key) { return (0, exports.markupComment)(markupPrefix, MarkupComments[key]); });
                                        shouldUpdate = removeMarkupOnly
                                            ? (0, exports.markupRegex)(markupPrefix)
                                            : RegExp(operationComments.join("|"), "g");
                                        return [4 /*yield*/, exists(path, shouldUpdate)];
                                    case 2:
                                        if (!_a.sent()) return [3 /*break*/, 4];
                                        before_1 = read(path);
                                        operationComments.forEach(function (operation) {
                                            if (before_1.includes(operation)) {
                                                comments.push(operation);
                                            }
                                        });
                                        if (!!dryRun) return [3 /*break*/, 4];
                                        return [4 /*yield*/, update(path, function (contents) {
                                                var result = contents;
                                                if (removeMarkupOnly) {
                                                    result = sanitize(result);
                                                }
                                                else {
                                                    result = updateFile(result, markupPrefix);
                                                    result = sanitize(result);
                                                }
                                                return result;
                                            })];
                                    case 3:
                                        _a.sent();
                                        _a.label = 4;
                                    case 4: return [2 /*return*/, { path: path, comments: comments }];
                                }
                            });
                        }); }))];
                case 1:
                    commentResults = _d.sent();
                    return [2 /*return*/, commentResults];
            }
        });
    });
}
exports.updateFiles = updateFiles;
//# sourceMappingURL=markup.js.map