"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const compiler_1 = require("@webruntime/compiler");
const compression_1 = __importDefault(require("compression"));
const express_1 = __importDefault(require("express"));
const helmet_1 = __importDefault(require("helmet"));
const path_1 = __importDefault(require("path"));
const uuidv4_1 = __importDefault(require("uuidv4"));
const colors_1 = __importDefault(require("colors"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const fast_xml_parser_1 = __importDefault(require("fast-xml-parser"));
const mime_types_1 = __importDefault(require("mime-types"));
const debug_1 = __importDefault(require("debug"));
const csurf_1 = __importDefault(require("csurf"));
const cookie_parser_1 = __importDefault(require("cookie-parser"));
const apexMiddleware_1 = require("./apexMiddleware");
const reload_1 = __importDefault(require("reload"));
const watch_1 = __importDefault(require("watch"));
const get_port_1 = __importDefault(require("get-port"));
const minimatch_1 = __importDefault(require("minimatch"));
const PUBLIC_DIR = 'public';
const debug = debug_1.default('localdevserver');
const FRAMEWORK_RESOURCE_JSON = require.resolve('@webruntime/framework/dist/resources.json');
const FRAMEWORK_OUTPUT_DIR = path_1.default.dirname(FRAMEWORK_RESOURCE_JSON);
const _WATCHTREE_FOLDERS = [];
let _RELOAD_RETURNED = { reload: () => { }, closeServer: async () => { } };
const FRAMEWORK_PUBLIC_DIR = `${FRAMEWORK_OUTPUT_DIR}/public/`;
const staticOptions = {
    index: false,
    immutable: true,
    maxAge: 31536000
};
function getRootApp(app, basePath) {
    if (basePath) {
        const rootApp = express_1.default();
        rootApp.use(basePath, app);
        return rootApp;
    }
    return app;
}
exports.getRootApp = getRootApp;
async function createServer(options, apiConfig = {}, connection) {
    const { templateDir, outputDir, basePath, srcDir } = await compiler_1.contextService.startContext(options);
    const sourceDir = path_1.default.resolve(srcDir);
    const app = express_1.default();
    app.use(compression_1.default());
    app.use(cspNonceMiddleware());
    app.use(cspPolicyMiddleware());
    app.use(cookie_parser_1.default());
    app.use(csurf_1.default({ cookie: true }));
    app.use(compiler_1.resourceMiddleware());
    app.get(`/assets/:versionKey/*`, salesforceStaticAssetsRoute(basePath));
    app.use(express_1.default.static(FRAMEWORK_PUBLIC_DIR, staticOptions));
    app.use(compiler_1.staticMiddleware());
    if (connection) {
        app.use(apexMiddleware_1.apexMiddleware({
            instanceUrl: connection.instanceUrl,
            accessToken: connection.accessToken
        }));
    }
    if (options.liveReload) {
        await startLiveReload(app, sourceDir, outputDir, templateDir);
    }
    app.use(compiler_1.apiMiddleware(apiConfig));
    return app;
}
exports.createServer = createServer;
async function startServer(app, basePath, port = 3000, onClose = () => { }) {
    app.get(`${basePath}/*`, compiler_1.templateMiddleware());
    app.use(compiler_1.compileErrorMiddleware());
    const server = getRootApp(app, basePath).listen(port, () => {
        console.log(colors_1.default.magenta.bold(`Server up on http://localhost:${server.address().port}${basePath}`));
    });
    server.on('close', async () => {
        compiler_1.contextService.endContext();
        if (_RELOAD_RETURNED) {
            await _RELOAD_RETURNED.closeServer();
        }
        _WATCHTREE_FOLDERS.forEach(watch_1.default.unwatchTree);
        onClose();
    });
    process.on('SIGINT', async () => {
        server.close();
        process.exit();
    });
    return server;
}
exports.startServer = startServer;
function cspNonceMiddleware() {
    return (req, res, next) => {
        res.locals.nonce = uuidv4_1.default();
        next();
    };
}
exports.cspNonceMiddleware = cspNonceMiddleware;
function cspPolicyMiddleware() {
    return helmet_1.default({
        contentSecurityPolicy: {
            directives: {
                scriptSrc: [
                    `'self'`,
                    (req, res) => `'nonce-${res.locals.nonce}'`
                ]
            }
        }
    });
}
exports.cspPolicyMiddleware = cspPolicyMiddleware;
function salesforceStaticAssetsRoute(basePath) {
    return (req, res, next) => {
        debug(req.url);
        if (req.url.indexOf('.') === -1) {
            req.url = `${basePath}/assets/${req.params[0]}`;
            const xmlFileName = '.localdevserver/public' + req.url + '.resource-meta.xml';
            if (fs_extra_1.default.existsSync(xmlFileName)) {
                const data = fs_extra_1.default.readFileSync(xmlFileName, 'utf8');
                const result = fast_xml_parser_1.default.parse(data);
                if (result) {
                    const contentType = result.StaticResource.contentType;
                    const fileExt = mime_types_1.default.extension(contentType);
                    req.url = req.url + '.' + fileExt;
                    next('route');
                }
            }
            else {
                next();
            }
        }
        else {
            if (req.url.indexOf('/assets/styles/') === -1 &&
                req.url.indexOf('/assets/fonts/') === -1 &&
                req.url.indexOf('/assets/icons/') === -1 &&
                req.url.indexOf('/assets/images/')) {
                req.url = `${basePath}/assets/${req.params[0]}`;
                next('route');
            }
            else {
                next();
            }
        }
    };
}
exports.salesforceStaticAssetsRoute = salesforceStaticAssetsRoute;
async function startLiveReload(app, sourceDir, outputDir, templateDir) {
    const reloading = {};
    const liveReloadPort = await get_port_1.default();
    debug('live reload port: ' + liveReloadPort);
    reload_1.default(app, {
        port: liveReloadPort,
        verbose: debug_1.default.enabled('localdevserver')
    }).then((reloadReturned) => {
        _RELOAD_RETURNED = reloadReturned;
        _WATCHTREE_FOLDERS.push(sourceDir);
        debug('watching: ' + sourceDir);
        const ignoreDirectoryPattern = new RegExp(outputDir + '.*');
        const watchCallback = function (file, curr, prev) {
            if (typeof file === 'object' && prev === null && curr === null) {
                return;
            }
            if (typeof file !== 'string') {
                return;
            }
            debug('file changed: ' + file);
            const forceIgnorePatterns = loadIgnorePatterns(path_1.default.join(templateDir, '.forceignore'));
            for (const ignorePattern of forceIgnorePatterns) {
                if (minimatch_1.default(file, ignorePattern)) {
                    return;
                }
            }
            if (!reloading[file]) {
                reloading[file] = true;
                _RELOAD_RETURNED.reload();
                setTimeout(() => {
                    reloading[file] = false;
                }, 500);
            }
            if (fs_extra_1.default.existsSync(file) && fs_extra_1.default.lstatSync(file).isDirectory()) {
                debug('new folder, now watching: ' + file);
                watch_1.default.watchTree(file, { ignoreDirectoryPattern }, watchCallback);
                _WATCHTREE_FOLDERS.push(file);
            }
            else if (_WATCHTREE_FOLDERS.indexOf(file) !== -1) {
                _WATCHTREE_FOLDERS.splice(_WATCHTREE_FOLDERS.indexOf(file), 1);
            }
        };
        watch_1.default.watchTree(sourceDir, { ignoreDirectoryPattern }, watchCallback);
    });
}
exports.startLiveReload = startLiveReload;
function loadIgnorePatterns(filepath) {
    if (!fs_extra_1.default.existsSync(filepath) || !fs_extra_1.default.lstatSync(filepath).isFile()) {
        return [];
    }
    return fs_extra_1.default
        .readFileSync(filepath)
        .toString()
        .split('\n')
        .filter(value => value.length > 0 && value.trim()[0] != '#');
}
//# sourceMappingURL=talonServerCopy.js.map