"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readExactProjectManifest = exports.tryReadProjectManifest = exports.readProjectManifestOnly = void 0;
const error_1 = require("@pnpm/error");
const write_project_manifest_1 = require("@pnpm/write-project-manifest");
const detectIndent = require("detect-indent");
const equal = require("fast-deep-equal");
const fs = require("fs");
const isWindows = require("is-windows");
const path = require("path");
const read_yaml_file_1 = require("read-yaml-file");
const sortKeys = require("sort-keys");
const util_1 = require("util");
const readFile_1 = require("./readFile");
const stat = util_1.promisify(fs.stat);
async function readProjectManifest(projectDir) {
    const result = await tryReadProjectManifest(projectDir);
    if (result.manifest !== null) {
        return result;
    }
    throw new error_1.default('NO_IMPORTER_MANIFEST_FOUND', `No package.json (or package.yaml, or package.json5) was found in "${projectDir}".`);
}
exports.default = readProjectManifest;
async function readProjectManifestOnly(projectDir) {
    const { manifest } = await readProjectManifest(projectDir);
    return manifest;
}
exports.readProjectManifestOnly = readProjectManifestOnly;
async function tryReadProjectManifest(projectDir) {
    try {
        const manifestPath = path.join(projectDir, 'package.json');
        const { data, text } = await readFile_1.readJsonFile(manifestPath);
        const { indent } = detectIndent(text);
        return {
            fileName: 'package.json',
            manifest: data,
            writeProjectManifest: createManifestWriter({
                indent,
                initialManifest: data,
                manifestPath,
            }),
        };
    }
    catch (err) {
        if (err.code !== 'ENOENT')
            throw err;
    }
    try {
        const manifestPath = path.join(projectDir, 'package.json5');
        const { data, text } = await readFile_1.readJson5File(manifestPath);
        const { indent } = detectIndent(text);
        return {
            fileName: 'package.json5',
            manifest: data,
            writeProjectManifest: createManifestWriter({
                indent,
                initialManifest: data,
                manifestPath,
            }),
        };
    }
    catch (err) {
        if (err.code !== 'ENOENT')
            throw err;
    }
    try {
        const manifestPath = path.join(projectDir, 'package.yaml');
        const manifest = await readPackageYaml(manifestPath);
        return {
            fileName: 'package.yaml',
            manifest,
            writeProjectManifest: createManifestWriter({ initialManifest: manifest, manifestPath }),
        };
    }
    catch (err) {
        if (err.code !== 'ENOENT')
            throw err;
    }
    if (isWindows()) {
        // ENOTDIR isn't used on Windows, but pnpm expects it.
        let s;
        try {
            s = await stat(projectDir);
        }
        catch (err) {
            // Ignore
        }
        if (s && !s.isDirectory()) {
            const err = new Error(`"${projectDir}" is not a directory`);
            err['code'] = 'ENOTDIR'; // tslint:disable-line
            throw err;
        }
    }
    const filePath = path.join(projectDir, 'package.json');
    return {
        fileName: 'package.json',
        manifest: null,
        writeProjectManifest: (manifest) => write_project_manifest_1.default(filePath, manifest),
    };
}
exports.tryReadProjectManifest = tryReadProjectManifest;
async function readExactProjectManifest(manifestPath) {
    const base = path.basename(manifestPath).toLowerCase();
    switch (base) {
        case 'package.json': {
            const { data, text } = await readFile_1.readJsonFile(manifestPath);
            const { indent } = detectIndent(text);
            return {
                manifest: data,
                writeProjectManifest: createManifestWriter({
                    indent,
                    initialManifest: data,
                    manifestPath,
                }),
            };
        }
        case 'package.json5': {
            const { data, text } = await readFile_1.readJson5File(manifestPath);
            const { indent } = detectIndent(text);
            return {
                manifest: data,
                writeProjectManifest: createManifestWriter({
                    indent,
                    initialManifest: data,
                    manifestPath,
                }),
            };
        }
        case 'package.yaml': {
            const manifest = await readPackageYaml(manifestPath);
            return {
                manifest,
                writeProjectManifest: createManifestWriter({ initialManifest: manifest, manifestPath }),
            };
        }
    }
    throw new Error(`Not supported manifest name "${base}"`);
}
exports.readExactProjectManifest = readExactProjectManifest;
async function readPackageYaml(filePath) {
    try {
        return await read_yaml_file_1.default(filePath);
    }
    catch (err) {
        if (err.name !== 'YAMLException')
            throw err;
        err.message += `\nin ${filePath}`;
        err['code'] = 'ERR_PNPM_YAML_PARSE';
        throw err;
    }
}
function createManifestWriter(opts) {
    const initialManifest = normalize(JSON.parse(JSON.stringify(opts.initialManifest)));
    return async (updatedManifest, force) => {
        updatedManifest = normalize(updatedManifest);
        if (force === true || !equal(initialManifest, updatedManifest)) {
            return write_project_manifest_1.default(opts.manifestPath, updatedManifest, { indent: opts.indent });
        }
    };
}
const dependencyKeys = new Set([
    'dependencies',
    'devDependencies',
    'optionalDependencies',
    'peerDependencies',
]);
function normalize(manifest) {
    const result = {};
    for (const key of Object.keys(manifest)) {
        if (!dependencyKeys.has(key)) {
            result[key] = manifest[key];
        }
        else if (Object.keys(manifest[key]).length !== 0) {
            result[key] = sortKeys(manifest[key]);
        }
    }
    return result;
}
//# sourceMappingURL=index.js.map