"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;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.classifyModuleScope = classifyModuleScope;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const strongFrontendDependencies = ['react', 'next', 'vue', '@angular/core', 'react-dom'];
const toolingFrontendDependencies = ['vite'];
const frontendDependencyPrefixes = ['@vitejs/'];
const strongBackendDependencies = ['express', 'fastify', '@nestjs/core', 'koa', 'hapi', 'tsoa'];
const frontendNameHints = ['web', 'frontend', 'ui-app', 'dashboard', 'console', 'portal', 'client'];
const backendNameHints = ['api', 'backend', 'service', 'worker', 'server', 'jobs', 'processor'];
const sharedNameHints = ['shared', 'common', 'platform', 'lib', 'libs', 'sdk', 'design-system', 'core'];
const packageLikeRoots = ['packages', 'libs'];
const uiSharedNameHints = ['ui', 'design-system'];
const backendEntrypoints = [
    'server.ts',
    'server.js',
    'main.ts',
    'main.js',
    'app.ts',
    'app.js',
    'src/server.ts',
    'src/server.js',
    'src/main.ts',
    'src/main.js',
    'src/app.ts',
    'src/app.js',
];
const workerEntrypoints = [
    'worker.ts',
    'worker.js',
    'process.ts',
    'process.js',
    'bootstrap.ts',
    'bootstrap.js',
];
const openApiArtifacts = [
    'openapi.yaml',
    'openapi.yml',
    'openapi.json',
    'swagger.yaml',
    'swagger.yml',
    'swagger.json',
    'api-contract.yaml',
    'api-contract.yml',
    'api-contract.json',
];
const indexEntrypoints = ['index.ts', 'index.js', 'src/index.ts', 'src/index.js'];
const jsxExtensions = new Set(['.tsx', '.jsx']);
function normalizePath(value) {
    return value.replace(/\\/g, '/').replace(/^\.\/+/u, '').replace(/\/+$/u, '').toLowerCase();
}
function pathExists(targetPath) {
    try {
        fs.accessSync(targetPath);
        return true;
    }
    catch {
        return false;
    }
}
function readJsonFileIfExists(filePath) {
    try {
        return JSON.parse(fs.readFileSync(filePath, { encoding: 'utf8' }));
    }
    catch {
        return undefined;
    }
}
function collectDependencyNames(packageJsonPath) {
    const parsed = readJsonFileIfExists(packageJsonPath);
    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
        return new Set();
    }
    const sections = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
    const dependencies = new Set();
    for (const section of sections) {
        const value = parsed[section];
        if (!value || typeof value !== 'object' || Array.isArray(value)) {
            continue;
        }
        for (const dependency of Object.keys(value)) {
            dependencies.add(dependency.toLowerCase());
        }
    }
    return dependencies;
}
function hasExportsField(packageJsonPath) {
    const parsed = readJsonFileIfExists(packageJsonPath);
    return !!parsed && typeof parsed === 'object' && !Array.isArray(parsed) && 'exports' in parsed;
}
function hasJsxEnabled(configPath) {
    const parsed = readJsonFileIfExists(configPath);
    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
        return false;
    }
    const compilerOptions = parsed.compilerOptions;
    if (!compilerOptions || typeof compilerOptions !== 'object' || Array.isArray(compilerOptions)) {
        return false;
    }
    const jsx = compilerOptions.jsx;
    return typeof jsx === 'string' && jsx.trim().length > 0;
}
function listPathSegments(modulePath) {
    return normalizePath(modulePath)
        .split('/')
        .filter((entry) => entry.length > 0);
}
function getCandidateRoots(workspaceRoot, modulePath) {
    const resolvedWorkspaceRoot = path.resolve(workspaceRoot);
    const moduleSegments = listPathSegments(modulePath);
    const isNestedWorkspacePackage = moduleSegments.length > 1 &&
        ['apps', 'packages', 'libs', 'services'].includes(moduleSegments[0]);
    const moduleRoot = path.resolve(workspaceRoot, ...moduleSegments);
    const candidates = [];
    let current = moduleRoot;
    while (current.startsWith(resolvedWorkspaceRoot)) {
        if (isNestedWorkspacePackage && current === resolvedWorkspaceRoot) {
            break;
        }
        candidates.push(current);
        if (current === resolvedWorkspaceRoot) {
            break;
        }
        const parent = path.dirname(current);
        if (parent === current) {
            break;
        }
        current = parent;
    }
    return candidates;
}
function hasAnyDependency(dependencies, names) {
    return names.some((name) => dependencies.has(name));
}
function hasDependencyPrefix(dependencies, prefixes) {
    for (const dependency of dependencies) {
        if (prefixes.some((prefix) => dependency.startsWith(prefix))) {
            return true;
        }
    }
    return false;
}
function hasAnyFile(candidateRoot, filePaths) {
    return filePaths.some((filePath) => pathExists(path.join(candidateRoot, ...filePath.split('/'))));
}
function scanForJsxFiles(candidateRoot) {
    const queue = [{ absolutePath: candidateRoot, depth: 0 }];
    while (queue.length > 0) {
        const current = queue.shift();
        if (!current) {
            continue;
        }
        let entries = [];
        try {
            entries = fs.readdirSync(current.absolutePath, { withFileTypes: true });
        }
        catch {
            continue;
        }
        for (const entry of entries) {
            const entryPath = path.join(current.absolutePath, entry.name);
            if (entry.isDirectory()) {
                if (current.depth >= 2 || entry.name === 'node_modules' || entry.name.startsWith('.')) {
                    continue;
                }
                queue.push({ absolutePath: entryPath, depth: current.depth + 1 });
                continue;
            }
            if (entry.isFile() && jsxExtensions.has(path.extname(entry.name).toLowerCase())) {
                return true;
            }
        }
    }
    return false;
}
function collectCandidateSignals(candidateRoot) {
    const packageJsonPath = path.join(candidateRoot, 'package.json');
    const dependencies = collectDependencyNames(packageJsonPath);
    return {
        dependencies,
        hasNextConfig: pathExists(path.join(candidateRoot, 'next.config.js')) ||
            pathExists(path.join(candidateRoot, 'next.config.mjs')) ||
            pathExists(path.join(candidateRoot, 'next.config.ts')),
        hasViteConfig: pathExists(path.join(candidateRoot, 'vite.config.js')) ||
            pathExists(path.join(candidateRoot, 'vite.config.ts')) ||
            pathExists(path.join(candidateRoot, 'vite.config.mjs')),
        hasIndexHtml: pathExists(path.join(candidateRoot, 'index.html')),
        hasPublicDir: pathExists(path.join(candidateRoot, 'public')),
        hasSrcComponents: pathExists(path.join(candidateRoot, 'src', 'components')),
        hasSrcPages: pathExists(path.join(candidateRoot, 'src', 'pages')),
        hasSrcApp: pathExists(path.join(candidateRoot, 'src', 'app')),
        hasJsxTsconfig: hasJsxEnabled(path.join(candidateRoot, 'tsconfig.json')) ||
            hasJsxEnabled(path.join(candidateRoot, 'jsconfig.json')),
        hasJsxFiles: scanForJsxFiles(candidateRoot),
        hasRoutesDir: pathExists(path.join(candidateRoot, 'routes')),
        hasControllersDir: pathExists(path.join(candidateRoot, 'controllers')),
        hasPrismaDir: pathExists(path.join(candidateRoot, 'prisma')),
        hasDbDir: pathExists(path.join(candidateRoot, 'db')),
        hasMigrationsDir: pathExists(path.join(candidateRoot, 'migrations')),
        hasApiDir: pathExists(path.join(candidateRoot, 'api')),
        hasServerEntrypoint: hasAnyFile(candidateRoot, backendEntrypoints),
        hasOpenApiArtifacts: hasAnyFile(candidateRoot, openApiArtifacts),
        hasWorkerEntrypoint: hasAnyFile(candidateRoot, workerEntrypoints),
        hasPackageJson: pathExists(packageJsonPath),
        hasExportsField: hasExportsField(packageJsonPath),
        hasIndexEntrypoint: hasAnyFile(candidateRoot, indexEntrypoints),
    };
}
function buildScoresForCandidate(input) {
    const signals = collectCandidateSignals(input.candidateRoot);
    const scores = { frontend: 0, backend: 0, shared: 0 };
    const weight = input.distanceFromModule === 0 ? 1 : input.distanceFromModule === 1 ? 0.95 : 0.85;
    const add = (scope, value) => {
        scores[scope] += value * weight;
    };
    if (hasAnyDependency(signals.dependencies, strongFrontendDependencies)) {
        add('frontend', 4);
    }
    if (hasAnyDependency(signals.dependencies, toolingFrontendDependencies)) {
        add('frontend', 2);
    }
    if (hasDependencyPrefix(signals.dependencies, frontendDependencyPrefixes)) {
        add('frontend', 2);
    }
    if (signals.hasNextConfig) {
        add('frontend', 5);
    }
    if (signals.hasViteConfig) {
        add('frontend', 4);
    }
    if (signals.hasIndexHtml) {
        add('frontend', 3);
    }
    if (signals.hasPublicDir) {
        add('frontend', 2);
    }
    if (signals.hasSrcComponents) {
        add('frontend', 2);
    }
    if (signals.hasSrcPages) {
        add('frontend', 2);
    }
    if (signals.hasSrcApp) {
        add('frontend', 2);
    }
    if (signals.hasJsxTsconfig) {
        add('frontend', 3);
    }
    if (signals.hasJsxFiles) {
        add('frontend', 3);
    }
    if (hasAnyDependency(signals.dependencies, strongBackendDependencies)) {
        add('backend', 4);
    }
    if (signals.hasRoutesDir) {
        add('backend', 3);
    }
    if (signals.hasControllersDir) {
        add('backend', 3);
    }
    if (signals.hasPrismaDir) {
        add('backend', 3);
    }
    if (signals.hasDbDir) {
        add('backend', 2);
    }
    if (signals.hasMigrationsDir) {
        add('backend', 2);
    }
    if (signals.hasApiDir) {
        add('backend', 2);
    }
    if (signals.hasServerEntrypoint) {
        add('backend', 4);
    }
    if (signals.hasOpenApiArtifacts) {
        add('backend', 3);
    }
    if (signals.hasWorkerEntrypoint) {
        add('backend', 2);
    }
    const segments = listPathSegments(input.modulePath);
    const moduleName = segments[segments.length - 1] ?? '';
    const isPackageLike = segments.some((segment) => packageLikeRoots.includes(segment));
    if (isPackageLike) {
        add('shared', 2);
    }
    if (signals.hasPackageJson) {
        add('shared', 1);
    }
    if (signals.hasExportsField) {
        add('shared', 3);
    }
    if (signals.hasIndexEntrypoint) {
        add('shared', 1);
    }
    if (!signals.hasServerEntrypoint && !signals.hasNextConfig && !signals.hasViteConfig && !signals.hasIndexHtml) {
        add('shared', 1);
    }
    if (sharedNameHints.includes(moduleName)) {
        add('shared', 3);
    }
    if (uiSharedNameHints.includes(moduleName) && isPackageLike) {
        add('shared', 2);
    }
    if (frontendNameHints.includes(moduleName)) {
        add('frontend', 1);
    }
    if (backendNameHints.includes(moduleName)) {
        add('backend', 1);
    }
    return scores;
}
function sumScores(scores) {
    return scores.reduce((accumulator, score) => ({
        frontend: accumulator.frontend + score.frontend,
        backend: accumulator.backend + score.backend,
        shared: accumulator.shared + score.shared,
    }), { frontend: 0, backend: 0, shared: 0 });
}
function resolveFromScores(input) {
    const entries = [
        { scope: 'frontend', score: input.scores.frontend },
        { scope: 'backend', score: input.scores.backend },
        { scope: 'shared', score: input.scores.shared },
    ].sort((left, right) => right.score - left.score);
    const strongest = entries[0];
    const second = entries[1];
    const projectKinds = new Set(input.projectKinds ?? []);
    const hasFrontendProject = projectKinds.has('frontend') || !!input.stacks?.frontendStack;
    const hasBackendProject = projectKinds.has('backend') || !!input.stacks?.backendStack;
    const hasLibraryProject = projectKinds.has('library') || !!input.stacks?.libraryStack;
    const isPackageLike = listPathSegments(input.modulePath).some((segment) => packageLikeRoots.includes(segment));
    if (strongest.score >= 5 && strongest.score - second.score >= 2) {
        return strongest.scope;
    }
    if (strongest.scope === 'shared' &&
        strongest.score >= 4 &&
        strongest.score >= second.score + 1 &&
        isPackageLike) {
        return 'shared';
    }
    if (input.scores.shared >= 4 &&
        input.scores.frontend < 4 &&
        input.scores.backend < 4 &&
        isPackageLike) {
        return 'shared';
    }
    if (input.scores.frontend >= 6 &&
        input.scores.frontend >= input.scores.backend + 2 &&
        input.scores.frontend >= input.scores.shared + 1) {
        return 'frontend';
    }
    if (input.scores.backend >= 5 &&
        input.scores.backend >= input.scores.frontend + 1 &&
        input.scores.backend >= input.scores.shared) {
        return 'backend';
    }
    if (hasLibraryProject && isPackageLike && input.scores.shared >= input.scores.frontend && input.scores.shared >= input.scores.backend) {
        return 'shared';
    }
    if (hasFrontendProject && !hasBackendProject && input.scores.frontend >= 4 && input.scores.frontend >= input.scores.shared) {
        return 'frontend';
    }
    if (hasBackendProject && input.scores.backend >= 3) {
        return 'backend';
    }
    return 'backend';
}
function classifyModuleScope(input) {
    if (!input.workspaceRoot) {
        const segments = listPathSegments(input.modulePath);
        const moduleName = segments[segments.length - 1] ?? '';
        if (sharedNameHints.includes(moduleName) || (uiSharedNameHints.includes(moduleName) && segments.some((segment) => packageLikeRoots.includes(segment)))) {
            return 'shared';
        }
        return 'backend';
    }
    const candidateRoots = getCandidateRoots(input.workspaceRoot, input.modulePath).filter((candidate) => pathExists(candidate));
    const scores = sumScores(candidateRoots.map((candidateRoot, index) => buildScoresForCandidate({
        candidateRoot,
        modulePath: input.modulePath,
        distanceFromModule: index,
    })));
    return resolveFromScores({
        modulePath: input.modulePath,
        projectKinds: input.projectKinds,
        stacks: input.stacks,
        scores,
    });
}
//# sourceMappingURL=moduleScope.js.map