"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SetExternalExtension = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const sync_1 = __importDefault(require("csv-parse/lib/sync"));
// Type definitions are undefined / documented and in flux for these. See the source.
// @ts-ignore
const parser_1 = require("nunjucks/src/parser");
// @ts-ignore
const lexer_1 = require("nunjucks/src/lexer");
const logger = __importStar(require("../../utils/logger"));
const acceptedFileTypes = ['json', 'csv'];
/**
 * Nunjucks extension for sourcing in variables from external sources.
 * Supports only .json and .csv sources for now.
 */
class SetExternalExtension {
    constructor(rootPath, env) {
        this.rootPath = rootPath;
        this.env = env;
        this.tags = ['ext'];
    }
    emitLoad(fullPath) {
        // Emit the nunjucks load event for the listener in {@link VariableRenderer}. No type defs.
        // @ts-ignore
        this.env.emit('load', '', { path: fullPath });
    }
    parse(parser, nodes, lexer) {
        // get the tag token
        const setExtTagToken = parser.nextToken();
        // parse the args and move after the block end. passing true
        // as the second arg is required if there are no parentheses
        const args = parser.parseSignature(null, true);
        parser.advanceAfterBlockEnd(setExtTagToken.value);
        const options = args.children
            .filter((child) => !(child instanceof nodes.KeywordArgs))
            .map((child) => child.value);
        // last child contains the kvp containing the path to the external variable source
        const lastChild = args.children[args.children.length - 1];
        const buffer = [];
        if (lastChild instanceof nodes.KeywordArgs) {
            lastChild.children.forEach((pair) => {
                const variableName = pair.key.value;
                const resourcePath = pair.value.value;
                acceptedFileTypes.forEach((fileType) => {
                    if (!resourcePath.endsWith(fileType)) {
                        return;
                    }
                    const fullResourcePath = path_1.default.resolve(this.rootPath, resourcePath);
                    if (fileType === 'json') {
                        const resourceRaw = fs_extra_1.default.readFileSync(fullResourcePath);
                        buffer.push(`{% set ${variableName} = ${resourceRaw} %}`);
                    }
                    else if (fileType === 'csv') {
                        const hasNoHeader = options.includes('noHeader');
                        const csvResourceRaw = (0, sync_1.default)(fs_extra_1.default.readFileSync(fullResourcePath), {
                            bom: true, // strip the byte order mark (BOM) from the input string or buffer.
                            columns: (hasNoHeader
                                ? false // if noHeader is present, first row is not header row
                                : header => header.map((col) => col)),
                        });
                        const resourceRaw = JSON.stringify(csvResourceRaw);
                        buffer.push(`{% set ${variableName} = ${resourceRaw} %}`);
                    }
                    this.emitLoad(fullResourcePath);
                });
            });
        }
        else {
            logger.error(`Invalid {% ext %} tag at line ${setExtTagToken.lineno}.`);
            return new nodes.NodeList(setExtTagToken.lineno, setExtTagToken.colno, []);
        }
        const newParser = new parser_1.Parser((0, lexer_1.lex)(buffer.join('\n'), lexer.opts));
        if (parser.extensions !== undefined) {
            newParser.extensions = parser.extensions;
        }
        return newParser.parse();
    }
}
exports.SetExternalExtension = SetExternalExtension;
//# sourceMappingURL=set-external.js.map