"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModuleFetcher = exports.ModuleReader = void 0;
const promises_1 = require("fs/promises");
const path_1 = require("path");
const resolve_1 = require("../resolve");
const exec_1 = require("../run/exec");
const assert_never_1 = require("../utils/assert-never");
const paths_1 = require("../utils/paths");
class ModuleReader {
    constructor() {
        this.fetcher = new ModuleFetcher();
    }
    async checkout(path) {
        await this.fetcher.init((0, resolve_1.parse)(path));
    }
    async sha(path) {
        const source = (0, resolve_1.parse)(path);
        const sha = await this.fetcher.sha(source);
        if (sha) {
            return sha;
        }
        throw new Error();
    }
    async read(path) {
        const source = (0, resolve_1.parse)(path);
        if (await this.fetcher.checkout(source)) {
            return (0, promises_1.readFile)((0, path_1.join)(this.fetcher.cwd(source), source.path), 'utf-8');
        }
        else {
            return null;
        }
    }
}
exports.ModuleReader = ModuleReader;
class ModuleFetcher {
    async init(source) {
        const cwd = this.cwd(source);
        const git = this.git(source);
        const dirStat = await (0, promises_1.stat)(cwd).catch((error) => {
            if ((error === null || error === void 0 ? void 0 : error.code) === 'ENOENT') {
                return null;
            }
            throw error;
        });
        if (!dirStat) {
            await (0, promises_1.mkdir)(cwd, { recursive: true });
            await git('init');
            await git('remote', 'add', 'origin', this.origin(source));
        }
        await this.refresh(source);
    }
    async sha(source) {
        const git = this.git(source);
        const sha = await git('rev-parse', 'FETCH_HEAD');
        if (sha) {
            return sha.trim();
        }
        throw new Error(`Could not find SHA for repository "${this.cwd(source)}"`);
    }
    async checkout(source) {
        const git = this.git(source);
        try {
            await git('checkout', 'FETCH_HEAD', '--', source.path);
        }
        catch (error) {
            if (error instanceof exec_1.ExecError && error.code === 1) {
                // "pathspec '.refloat/config.js' did not match any file(s) known to git"
                return false;
            }
            throw error;
        }
        return true;
    }
    async refresh(source) {
        var _a;
        const git = this.git(source);
        await git('fetch', 'origin', (_a = source.ref) !== null && _a !== void 0 ? _a : 'HEAD');
    }
    cwd({ host, owner, repo, ref }) {
        return (0, path_1.join)(paths_1.paths.cache, 'modules', host, owner, repo, ref !== null && ref !== void 0 ? ref : '~default');
    }
    git(source) {
        const cwd = this.cwd(source);
        return (...args) => exec_1.exec.run('git', args, { cwd });
    }
    origin({ scheme, host, owner, repo }) {
        if (scheme === 'git+https') {
            return `https://${host}/${owner}/${repo}.git`;
        }
        else if (scheme === 'git+ssh') {
            return `git@${host}:${owner}/${repo}.git`;
        }
        else {
            return (0, assert_never_1.assertNever)(scheme);
        }
    }
}
exports.ModuleFetcher = ModuleFetcher;
//# sourceMappingURL=fetch.js.map