"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAutoImportProviderProjectStatic = void 0;
const project_1 = require("./project");
function createAutoImportProviderProjectStatic(tsBase, host, createLanguageService) {
    const ts = tsBase;
    const { combinePaths, inferredTypesContainingFile, arrayFrom, resolvePackageNameToPackageJson, concatenate, forEach, startsWith, getEntrypointsFromPackageJsonInfo, mapDefined, timestamp, } = ts;
    return {
        maxDependencies: 10,
        compilerOptionsOverrides: {
            diagnostics: false,
            skipLibCheck: true,
            sourceMap: false,
            types: ts.emptyArray,
            lib: ts.emptyArray,
            noLib: true,
        },
        getRootFileNames(dependencySelection, hostProject, moduleResolutionHost, compilerOptions) {
            var _a, _b;
            if (!dependencySelection) {
                return ts.emptyArray;
            }
            const program = hostProject.getCurrentProgram();
            if (!program) {
                return ts.emptyArray;
            }
            const start = timestamp();
            let dependencyNames;
            let rootNames;
            const rootFileName = combinePaths(hostProject.currentDirectory, inferredTypesContainingFile);
            const packageJsons = hostProject.getPackageJsonsForAutoImport(combinePaths(hostProject.currentDirectory, rootFileName));
            for (const packageJson of packageJsons) {
                (_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a.forEach((_, dependenyName) => addDependency(dependenyName));
                (_b = packageJson.peerDependencies) === null || _b === void 0 ? void 0 : _b.forEach((_, dependencyName) => addDependency(dependencyName));
            }
            let dependenciesAdded = 0;
            if (dependencyNames) {
                const symlinkCache = hostProject.getSymlinkCache();
                for (const name of arrayFrom(dependencyNames.keys())) {
                    // Avoid creating a large project that would significantly slow down time to editor interactivity
                    if (dependencySelection === 2 /* PackageJsonAutoImportPreference.Auto */ && dependenciesAdded > this.maxDependencies) {
                        hostProject.log(`AutoImportProviderProject: attempted to add more than ${this.maxDependencies} dependencies. Aborting.`);
                        return ts.emptyArray;
                    }
                    // 1. Try to load from the implementation package. For many dependencies, the
                    //	package.json will exist, but the package will not contain any typings,
                    //	so `entrypoints` will be undefined. In that case, or if the dependency
                    //	is missing altogether, we will move on to trying the @types package (2).
                    const packageJson = resolvePackageNameToPackageJson(name, hostProject.currentDirectory, compilerOptions, moduleResolutionHost, 
                    // @ts-expect-error
                    program.getModuleResolutionCache());
                    if (packageJson) {
                        const entrypoints = getRootNamesFromPackageJson(packageJson, program, symlinkCache);
                        if (entrypoints) {
                            rootNames = concatenate(rootNames, entrypoints);
                            dependenciesAdded += entrypoints.length ? 1 : 0;
                            continue;
                        }
                    }
                    // 2. Try to load from the @types package in the tree and in the global
                    //	typings cache location, if enabled.
                    // @ts-expect-error
                    const done = forEach([hostProject.currentDirectory, hostProject.getGlobalTypingsCacheLocation()], (directory) => {
                        if (directory) {
                            const typesPackageJson = resolvePackageNameToPackageJson(`@types/${name}`, directory, compilerOptions, moduleResolutionHost, 
                            // @ts-expect-error
                            program.getModuleResolutionCache());
                            if (typesPackageJson) {
                                const entrypoints = getRootNamesFromPackageJson(typesPackageJson, program, symlinkCache);
                                rootNames = concatenate(rootNames, entrypoints);
                                dependenciesAdded += (entrypoints === null || entrypoints === void 0 ? void 0 : entrypoints.length) ? 1 : 0;
                                return true;
                            }
                        }
                    });
                    if (done)
                        continue;
                    // 3. If the @types package did not exist and the user has settings that
                    //	allow processing JS from node_modules, go back to the implementation
                    //	package and load the JS.
                    if (packageJson && compilerOptions.allowJs && compilerOptions.maxNodeModuleJsDepth) {
                        const entrypoints = getRootNamesFromPackageJson(packageJson, program, symlinkCache, /*allowJs*/ true);
                        rootNames = concatenate(rootNames, entrypoints);
                        dependenciesAdded += (entrypoints === null || entrypoints === void 0 ? void 0 : entrypoints.length) ? 1 : 0;
                    }
                }
            }
            if (rootNames === null || rootNames === void 0 ? void 0 : rootNames.length) {
                hostProject.log(`AutoImportProviderProject: found ${rootNames.length} root files in ${dependenciesAdded} dependencies in ${timestamp() - start} ms`);
            }
            return rootNames || ts.emptyArray;
            function addDependency(dependency) {
                if (!startsWith(dependency, '@types/')) {
                    (dependencyNames || (dependencyNames = new Set())).add(dependency);
                }
            }
            function getRootNamesFromPackageJson(packageJson, program, symlinkCache, resolveJs) {
                var _a;
                const entrypoints = getEntrypointsFromPackageJsonInfo(packageJson, compilerOptions, moduleResolutionHost, 
                // @ts-expect-error
                program.getModuleResolutionCache(), resolveJs);
                if (entrypoints) {
                    // some packages have giant exports maps, don't add them to the project to not slow down the editor
                    if (entrypoints.length > 100)
                        return;
                    const real = (_a = moduleResolutionHost.realpath) === null || _a === void 0 ? void 0 : _a.call(moduleResolutionHost, packageJson.packageDirectory);
                    const isSymlink = real && real !== packageJson.packageDirectory;
                    if (isSymlink) {
                        symlinkCache.setSymlinkedDirectory(packageJson.packageDirectory, {
                            real,
                            realPath: hostProject.toPath(real),
                        });
                    }
                    // @ts-expect-error
                    return mapDefined(entrypoints, (entrypoint) => {
                        const resolvedFileName = isSymlink ? entrypoint.replace(packageJson.packageDirectory, real) : entrypoint;
                        if (!program.getSourceFile(resolvedFileName) && !(isSymlink && program.getSourceFile(entrypoint))) {
                            return resolvedFileName;
                        }
                    });
                }
            }
        },
        create(dependencySelection, hostProject, moduleResolutionHost) {
            if (dependencySelection === 0 /* PackageJsonAutoImportPreference.Off */) {
                return undefined;
            }
            const compilerOptions = Object.assign(Object.assign({}, hostProject.getCompilerOptions()), this.compilerOptionsOverrides);
            let rootNames = this.getRootFileNames(dependencySelection, hostProject, moduleResolutionHost, compilerOptions);
            if (!rootNames.length) {
                return undefined;
            }
            return createAutoImportProviderProject(tsBase, host, createLanguageService, { self: this, hostProject, rootNames, compilerOptions });
        }
    };
}
exports.createAutoImportProviderProjectStatic = createAutoImportProviderProjectStatic;
function createAutoImportProviderProject(tsBase, host, createLanguageService, options) {
    const { self, rootNames, compilerOptions, hostProject } = options;
    const ts = tsBase;
    const { some } = ts;
    const project = Object.assign(Object.assign({}, (0, project_1.createProject)(tsBase, host, createLanguageService, {
        projectService: hostProject.projectService,
        currentDirectory: hostProject.currentDirectory,
        compilerOptions,
    })), { projectVersion: 0, getProjectVersion() {
            return this.projectVersion.toString();
        }, rootFileNames: rootNames, hostProject,
        isEmpty() {
            return !some(this.rootFileNames);
        },
        isOrphan() {
            return true;
        },
        updateGraph() {
            var _a;
            let rootFileNames = this.rootFileNames;
            if (!rootFileNames) {
                rootFileNames = self.getRootFileNames(this.hostProject.includePackageJsonAutoImports(), this.hostProject, this.hostProject.getModuleResolutionHostForAutoImportProvider(), this.getCompilationSettings());
            }
            this.rootFileNames = rootFileNames;
            const oldProgram = this.getCurrentProgram();
            this.program = (_a = this.languageService) === null || _a === void 0 ? void 0 : _a.getProgram();
            this.dirty = false;
            if (oldProgram && oldProgram !== this.getCurrentProgram()) {
                this.hostProject.clearCachedExportInfoMap();
            }
        },
        scheduleInvalidateResolutionsOfFailedLookupLocations() {
            // Invalidation will happen on-demand as part of updateGraph
            return;
        },
        hasRoots() {
            var _a;
            return !!((_a = this.rootFileNames) === null || _a === void 0 ? void 0 : _a.length);
        },
        markAsDirty() {
            if (!this.dirty) {
                this.rootFileNames = undefined;
                this.projectVersion++;
                this.dirty = true;
            }
        },
        getScriptFileNames() {
            return this.rootFileNames || ts.emptyArray;
        },
        getLanguageService() {
            throw new Error("AutoImportProviderProject language service should never be used. To get the program, use `project.getCurrentProgram()`.");
        },
        onAutoImportProviderSettingsChanged() {
            throw new Error("AutoImportProviderProject is an auto import provider; use `markAsDirty()` instead.");
        },
        onPackageJsonChange() {
            throw new Error("package.json changes should be notified on an AutoImportProvider's host project");
        },
        getModuleResolutionHostForAutoImportProvider() {
            throw new Error("AutoImportProviderProject cannot provide its own host; use `hostProject.getModuleResolutionHostForAutomImportProvider()` instead.");
        },
        includePackageJsonAutoImports() {
            return 0 /* PackageJsonAutoImportPreference.Off */;
        },
        getTypeAcquisition() {
            return { enable: false };
        },
        getSymlinkCache() {
            return this.hostProject.getSymlinkCache();
        },
        getModuleResolutionCache() {
            var _a, _b;
            // @ts-expect-error
            return (_b = (_a = this.hostProject.languageService) === null || _a === void 0 ? void 0 : _a.getProgram()) === null || _b === void 0 ? void 0 : _b.getModuleResolutionCache();
        } });
    return (0, project_1.initProject)(project, new Proxy(host, {
        get(target, key) {
            return key in project ? project[key] : target[key];
        },
        set(_target, key, value) {
            project[key] = value;
            return true;
        }
    }), createLanguageService);
}
