import path from 'path'; import type {FileSystem} from '@atlaspack/types-internal'; import {fuzzySearch} from './schema'; import {relativePath} from './path'; import {resolveConfig} from './config'; export async function findAlternativeNodeModules( fs: FileSystem, moduleName: string, dir: string, ): Promise> { let potentialModules: Array = []; let root = path.parse(dir).root; let isOrganisationModule = moduleName.startsWith('@'); while (dir !== root) { // Skip node_modules directories if (path.basename(dir) === 'node_modules') { dir = path.dirname(dir); } try { let modulesDir = path.join(dir, 'node_modules'); let stats = await fs.stat(modulesDir); if (stats.isDirectory()) { let dirContent = (await fs.readdir(modulesDir)).sort(); // Filter out the modules that interest us let modules = dirContent.filter((i) => isOrganisationModule ? i.startsWith('@') : !i.startsWith('@'), ); // If it's an organisation module, loop through all the modules of that organisation if (isOrganisationModule) { await Promise.all( modules.map(async (item) => { let orgDirPath = path.join(modulesDir, item); let orgDirContent = (await fs.readdir(orgDirPath)).sort(); // Add all org packages potentialModules.push( ...orgDirContent.map((i) => `${item}/${i}`), ); }), ); } else { potentialModules.push(...modules); } } } catch (err: any) { // ignore } // Move up a directory dir = path.dirname(dir); } return fuzzySearch(potentialModules.sort(), moduleName).slice(0, 2); } async function findAllFilesUp({ fs, dir, root, basedir, maxlength, collected, leadingDotSlash = true, includeDirectories = true, }: { fs: FileSystem; dir: string; root: string; basedir: string; maxlength: number; collected: Array; leadingDotSlash?: boolean; includeDirectories?: boolean; }): Promise { let dirContent = (await fs.readdir(dir)).sort(); return Promise.all( dirContent.map(async (item) => { let fullPath = path.join(dir, item); let relativeFilePath = relativePath(basedir, fullPath, leadingDotSlash); if (relativeFilePath.length < maxlength) { let stats = await fs.stat(fullPath); let isDir = stats.isDirectory(); if ((isDir && includeDirectories) || stats.isFile()) { collected.push(relativeFilePath); } // If it's a directory, run over each item within said directory... if (isDir) { return findAllFilesUp({ fs, dir: fullPath, root, basedir, maxlength, collected, }); } } }), ); } export async function findAlternativeFiles( fs: FileSystem, fileSpecifier: string, dir: string, projectRoot: string, leadingDotSlash: boolean = true, includeDirectories: boolean = true, includeExtension: boolean = false, ): Promise> { let potentialFiles: Array = []; // Find our root, we won't recommend files above the package root as that's bad practise let pkg = await resolveConfig( fs, path.join(dir, 'index'), ['package.json'], projectRoot, ); let pkgRoot = pkg ? path.dirname(pkg) : projectRoot; await findAllFilesUp({ fs, dir: pkgRoot, root: pkgRoot, basedir: dir, maxlength: fileSpecifier.length + 10, collected: potentialFiles, leadingDotSlash, includeDirectories, }); if (path.extname(fileSpecifier) === '' && !includeExtension) { potentialFiles = potentialFiles.map((p) => { let ext = path.extname(p); return ext.length > 0 ? p.slice(0, -ext.length) : p; }); } return fuzzySearch(potentialFiles.sort(), fileSpecifier).slice(0, 2); }