"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNodesV2 = void 0;
exports.buildPackageJsonPatterns = buildPackageJsonPatterns;
exports.buildPackageJsonWorkspacesMatcher = buildPackageJsonWorkspacesMatcher;
exports.createNodeFromPackageJson = createNodeFromPackageJson;
exports.buildProjectConfigurationFromPackageJson = buildProjectConfigurationFromPackageJson;
exports.getGlobPatternsFromPackageManagerWorkspaces = getGlobPatternsFromPackageManagerWorkspaces;
const minimatch_1 = require("minimatch");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const nx_json_1 = require("../../config/nx-json");
const to_project_name_1 = require("../../config/to-project-name");
const fileutils_1 = require("../../utils/fileutils");
const globs_1 = require("../../utils/globs");
const logger_1 = require("../../utils/logger");
const output_1 = require("../../utils/output");
const package_json_1 = require("../../utils/package-json");
const path_1 = require("../../utils/path");
const versions_1 = require("../../utils/versions");
const plugins_1 = require("../../project-graph/plugins");
const path_2 = require("path");
const file_hasher_1 = require("../../hasher/file-hasher");
const package_json_2 = require("../../../plugins/package-json");
const globPatterns = (0, globs_1.combineGlobPatterns)('package.json', '**/package.json', 'project.json', '**/project.json');
exports.createNodesV2 = [
    globPatterns,
    (configFiles, _, context) => {
        const { packageJsons, projectJsonRoots } = splitConfigFiles(configFiles);
        const readJson = (f) => (0, fileutils_1.readJsonFile)((0, node_path_1.join)(context.workspaceRoot, f));
        let isInPackageJsonWorkspaces = (p) => true;
        if (process.env.NX_INFER_ALL_PACKAGE_JSONS !== 'true' ||
            configFiles.includes('package.json')) {
            const patterns = buildPackageJsonPatterns(context.workspaceRoot, readJson);
            isInPackageJsonWorkspaces = buildPackageJsonWorkspacesMatcher(patterns);
        }
        const isNextToProjectJson = (packageJsonPath) => {
            return projectJsonRoots.has((0, node_path_1.dirname)(packageJsonPath));
        };
        const cache = (0, package_json_2.readPackageJsonConfigurationCache)();
        return (0, plugins_1.createNodesFromFiles)((packageJsonPath, options, context) => {
            const isInPackageManagerWorkspaces = isInPackageJsonWorkspaces(packageJsonPath);
            if (!isInPackageManagerWorkspaces &&
                !isNextToProjectJson(packageJsonPath)) {
                // Skip if package.json is not part of the package.json workspaces and not next to a project.json.
                return null;
            }
            return createNodeFromPackageJson(packageJsonPath, context.workspaceRoot, cache, isInPackageManagerWorkspaces);
        }, packageJsons, _, context);
    },
];
function splitConfigFiles(configFiles) {
    const packageJsons = [];
    const projectJsonRoots = new Set();
    for (const configFile of configFiles) {
        if ((0, path_2.basename)(configFile) === 'package.json') {
            packageJsons.push(configFile);
        }
        else {
            projectJsonRoots.add((0, node_path_1.dirname)(configFile));
        }
    }
    return { packageJsons, projectJsonRoots };
}
function buildPackageJsonPatterns(workspaceRoot, readJson) {
    const patterns = getGlobPatternsFromPackageManagerWorkspaces(workspaceRoot, readJson);
    const negativePatterns = [];
    const positivePatterns = [];
    const positivePatternLookup = {};
    const negativePatternLookup = {};
    for (const pattern of patterns) {
        if (pattern.startsWith('!')) {
            negativePatterns.push(pattern);
            negativePatternLookup[pattern.slice(1)] = true;
        }
        else {
            positivePatterns.push(pattern);
            positivePatternLookup[pattern] = true;
        }
    }
    if (
    // There are some negative patterns
    negativePatterns.length > 0 &&
        // No positive patterns
        (positivePatterns.length === 0 ||
            // Or only a single positive pattern that is the default coming from root package
            (positivePatterns.length === 1 && positivePatterns[0] === 'package.json'))) {
        positivePatterns.push('**/package.json');
    }
    return {
        positive: positivePatterns,
        positiveLookup: positivePatternLookup,
        negative: negativePatterns,
        negativeLookup: negativePatternLookup,
    };
}
function buildPackageJsonWorkspacesMatcher(patterns) {
    return (p) => 
    // use lookup to avoid unnecessary minimatch calls
    (patterns.positiveLookup[p] ||
        patterns.positive.some((positive) => (0, minimatch_1.minimatch)(p, positive))) &&
        /**
         * minimatch will return true if the given p is NOT excluded by the negative pattern.
         *
         * For example if the negative pattern is "!packages/vite", then the given p "packages/vite" will return false,
         * the given p "packages/something-else/package.json" will return true.
         *
         * Therefore, we need to ensure that every negative pattern returns true to validate that the given p is not
         * excluded by any of the negative patterns.
         */
        !patterns.negativeLookup[p] &&
        patterns.negative.every((negative) => (0, minimatch_1.minimatch)(p, negative));
}
function createNodeFromPackageJson(pkgJsonPath, workspaceRoot, cache, isInPackageManagerWorkspaces) {
    const json = (0, fileutils_1.readJsonFile)((0, node_path_1.join)(workspaceRoot, pkgJsonPath));
    const projectRoot = (0, node_path_1.dirname)(pkgJsonPath);
    const hash = (0, file_hasher_1.hashObject)({
        ...json,
        root: projectRoot,
        isInPackageManagerWorkspaces,
        nxVersion: versions_1.nxVersion,
    });
    const cached = cache[hash];
    if (cached) {
        return {
            projects: {
                [cached.root]: cached,
            },
        };
    }
    const project = buildProjectConfigurationFromPackageJson(json, workspaceRoot, pkgJsonPath, (0, nx_json_1.readNxJson)(workspaceRoot), isInPackageManagerWorkspaces);
    cache[hash] = project;
    return {
        projects: {
            [project.root]: project,
        },
    };
}
function buildProjectConfigurationFromPackageJson(packageJson, workspaceRoot, packageJsonPath, nxJson, isInPackageManagerWorkspaces) {
    const normalizedPath = packageJsonPath.split('\\').join('/');
    const projectRoot = (0, node_path_1.dirname)(normalizedPath);
    const siblingProjectJson = tryReadJson((0, node_path_1.join)(workspaceRoot, projectRoot, 'project.json'));
    if (siblingProjectJson) {
        for (const target of Object.keys(siblingProjectJson?.targets ?? {})) {
            const { executor, command, options } = siblingProjectJson.targets[target];
            if (
            // will use run-commands, different target
            command ||
                // Either uses a different executor or runs a different script
                (executor &&
                    (executor !== 'nx:run-script' || options?.script !== target))) {
                delete packageJson.scripts?.[target];
            }
        }
    }
    if (!packageJson.name && projectRoot === '.' && !packageJson.nx?.name) {
        throw new Error('Nx requires the root package.json to specify a name if it is being used as an Nx project.');
    }
    let name = packageJson.name ?? (0, to_project_name_1.toProjectName)(normalizedPath);
    const projectConfiguration = {
        root: projectRoot,
        name,
        ...packageJson.nx,
        targets: (0, package_json_1.readTargetsFromPackageJson)(packageJson, nxJson, projectRoot, workspaceRoot),
        tags: (0, package_json_1.getTagsFromPackageJson)(packageJson),
        metadata: (0, package_json_1.getMetadataFromPackageJson)(packageJson, isInPackageManagerWorkspaces),
    };
    if (nxJson?.workspaceLayout?.appsDir != nxJson?.workspaceLayout?.libsDir &&
        nxJson?.workspaceLayout?.appsDir &&
        projectRoot.startsWith(nxJson.workspaceLayout.appsDir)) {
        projectConfiguration.projectType = 'application';
    }
    else if (typeof nxJson?.workspaceLayout?.libsDir !== 'undefined' &&
        projectRoot.startsWith(nxJson.workspaceLayout.libsDir)) {
        projectConfiguration.projectType = 'library';
    }
    return projectConfiguration;
}
/**
 * Get the package.json globs from package manager workspaces
 */
function getGlobPatternsFromPackageManagerWorkspaces(root, 
// allow overwriting these args so we can use them in devkit
readJson = (path) => (0, fileutils_1.readJsonFile)((0, node_path_1.join)(root, path)), readYaml = (path) => (0, fileutils_1.readYamlFile)((0, node_path_1.join)(root, path)), exists = (p) => (0, node_fs_1.existsSync)((0, node_path_1.join)(root, p))) {
    try {
        const patterns = [];
        const packageJson = readJson('package.json');
        patterns.push(...normalizePatterns(Array.isArray(packageJson.workspaces)
            ? packageJson.workspaces
            : (packageJson.workspaces?.packages ?? [])));
        if (exists('pnpm-workspace.yaml')) {
            try {
                const { packages } = readYaml('pnpm-workspace.yaml') ?? {};
                patterns.push(...normalizePatterns(packages || []));
            }
            catch (e) {
                output_1.output.warn({
                    title: `${logger_1.NX_PREFIX} Unable to parse pnpm-workspace.yaml`,
                    bodyLines: [e.toString()],
                });
            }
        }
        if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, 'lerna.json'))) {
            try {
                const { packages } = readJson('lerna.json') ?? {};
                patterns.push(...normalizePatterns(packages?.length > 0 ? packages : ['packages/*']));
            }
            catch (e) {
                output_1.output.warn({
                    title: `${logger_1.NX_PREFIX} Unable to parse lerna.json`,
                    bodyLines: [e.toString()],
                });
            }
        }
        // Merge patterns from workspaces definitions
        // TODO(@AgentEnder): update logic after better way to determine root project inclusion
        // Include the root project
        return packageJson.nx ? patterns.concat('package.json') : patterns;
    }
    catch {
        return [];
    }
}
function normalizePatterns(patterns) {
    return patterns.map((pattern) => removeRelativePath(pattern.endsWith('/package.json')
        ? pattern
        : (0, path_1.joinPathFragments)(pattern, 'package.json')));
}
function removeRelativePath(pattern) {
    return pattern.startsWith('./') ? pattern.substring(2) : pattern;
}
function tryReadJson(path) {
    try {
        return (0, fileutils_1.readJsonFile)(path);
    }
    catch {
        return null;
    }
}
