outfitter
Version:
Command-line tool for equipping your development journey with configurations and fieldguides
187 lines • 7.43 kB
JavaScript
import fsExtra from 'fs-extra';
const { pathExists } = fsExtra;
import { readFile } from 'fs/promises';
import { join } from 'path';
/**
* Determines whether a given npm package is listed as a dependency in the project's `package.json`.
*
* Checks for the presence of the specified package in the `dependencies`, `devDependencies`, or `peerDependencies` fields of `package.json` within the provided directory.
*
* @param packageName - The name of the npm package to check for.
* @param cwd - The directory to search in. Defaults to the current working directory.
* @returns `true` if the package is found in any dependency section; otherwise, `false`.
*/
async function hasPackage(packageName, cwd = process.cwd()) {
try {
const packageJsonPath = join(cwd, 'package.json');
if (await pathExists(packageJsonPath)) {
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
const deps = {
...(packageJson.dependencies ?? {}),
...(packageJson.devDependencies ?? {}),
...(packageJson.peerDependencies ?? {}),
};
return packageName in deps;
}
}
catch {
// Ignore errors
}
return false;
}
/**
* Checks if a file exists at the specified path within a given directory.
*
* @param filePath - Relative path to the file to check.
* @param cwd - Directory to resolve {@link filePath} from. Defaults to the current working directory.
* @returns `true` if the file exists, otherwise `false`.
*/
async function fileExists(filePath, cwd = process.cwd()) {
return pathExists(join(cwd, filePath));
}
/**
* Detects the presence of common frameworks, languages, tools, and features in a project directory.
*
* Inspects the specified directory for configuration files and package dependencies to determine which development environment features are present. Returns a {@link TerrainFeatures} object with boolean flags for each detected feature.
*
* @param cwd - The directory to analyze. Defaults to the current working directory.
* @returns An object indicating detected frameworks, languages, testing tools, state management libraries, build tools, project features, and package managers.
*/
export async function detectTerrain(cwd = process.cwd()) {
// Run all checks in parallel for better performance
const [
// Framework files
nextConfigJs, nextConfigMjs, nextConfigTs,
// Project files
tsconfigExists, packageJsonExists, requirementsTxt, pyprojectToml, pipfile, vitestConfigTs, jestConfigJs, playwrightConfigTs, cypressConfigJs, viteConfigTs, webpackConfigJs, pnpmWorkspace, lernaJson, nxJson, rushJson, dockerfile, dockerCompose, githubWorkflows, gitlabCi, pnpmLock, yarnLock, packageLock, bunLock,
// Packages
hasNext, hasReact, hasVue, hasSvelte, hasAngular, hasVitest, hasJest, hasPlaywright, hasCypress, hasZustand, hasRedux, hasReduxToolkit, hasMobx, hasVite, hasWebpack,] = await Promise.all([
// Framework files
fileExists('next.config.js', cwd),
fileExists('next.config.mjs', cwd),
fileExists('next.config.ts', cwd),
// Project files
fileExists('tsconfig.json', cwd),
fileExists('package.json', cwd),
fileExists('requirements.txt', cwd),
fileExists('pyproject.toml', cwd),
fileExists('Pipfile', cwd),
fileExists('vitest.config.ts', cwd),
fileExists('jest.config.js', cwd),
fileExists('playwright.config.ts', cwd),
fileExists('cypress.config.js', cwd),
fileExists('vite.config.ts', cwd),
fileExists('webpack.config.js', cwd),
fileExists('pnpm-workspace.yaml', cwd),
fileExists('lerna.json', cwd),
fileExists('nx.json', cwd),
fileExists('rush.json', cwd),
fileExists('Dockerfile', cwd),
fileExists('docker-compose.yml', cwd),
fileExists('.github/workflows', cwd),
fileExists('.gitlab-ci.yml', cwd),
fileExists('pnpm-lock.yaml', cwd),
fileExists('yarn.lock', cwd),
fileExists('package-lock.json', cwd),
fileExists('bun.lockb', cwd),
// Packages
hasPackage('next', cwd),
hasPackage('react', cwd),
hasPackage('vue', cwd),
hasPackage('svelte', cwd),
hasPackage('@angular/core', cwd),
hasPackage('vitest', cwd),
hasPackage('jest', cwd),
hasPackage('@playwright/test', cwd),
hasPackage('cypress', cwd),
hasPackage('zustand', cwd),
hasPackage('redux', cwd),
hasPackage('@reduxjs/toolkit', cwd),
hasPackage('mobx', cwd),
hasPackage('vite', cwd),
hasPackage('webpack', cwd),
]);
const terrain = {
// Frameworks
nextjs: nextConfigJs || nextConfigMjs || nextConfigTs || hasNext,
react: hasReact,
vue: hasVue,
svelte: hasSvelte,
angular: hasAngular,
// Languages/Runtime
typescript: tsconfigExists,
javascript: packageJsonExists,
node: packageJsonExists,
python: requirementsTxt || pyprojectToml || pipfile,
// Testing
vitest: hasVitest || vitestConfigTs,
jest: hasJest || jestConfigJs,
playwright: hasPlaywright || playwrightConfigTs,
cypress: hasCypress || cypressConfigJs,
// State Management
zustand: hasZustand,
redux: hasRedux || hasReduxToolkit,
mobx: hasMobx,
// Build Tools
vite: hasVite || viteConfigTs,
webpack: hasWebpack || webpackConfigJs,
// Features
monorepo: pnpmWorkspace || lernaJson || nxJson || rushJson,
docker: dockerfile || dockerCompose,
githubActions: githubWorkflows,
gitlabCi: gitlabCi,
// Package Manager
pnpm: pnpmLock,
yarn: yarnLock,
npm: packageLock,
bun: bunLock,
};
return terrain;
}
export function getTerrainSummary(terrain) {
const features = [];
// Primary framework
if (terrain.nextjs)
features.push('Next.js application');
else if (terrain.react)
features.push('React application');
else if (terrain.vue)
features.push('Vue application');
else if (terrain.svelte)
features.push('Svelte application');
else if (terrain.angular)
features.push('Angular application');
// Language
if (terrain.typescript)
features.push('TypeScript');
if (terrain.python)
features.push('Python');
// Testing
if (terrain.vitest)
features.push('Vitest testing');
else if (terrain.jest)
features.push('Jest testing');
if (terrain.playwright)
features.push('Playwright e2e');
else if (terrain.cypress)
features.push('Cypress e2e');
// State management
if (terrain.zustand)
features.push('Zustand state');
else if (terrain.redux)
features.push('Redux state');
else if (terrain.mobx)
features.push('MobX state');
// Build tools
if (terrain.vite)
features.push('Vite bundler');
else if (terrain.webpack)
features.push('Webpack bundler');
// Special features
if (terrain.monorepo)
features.push('Monorepo structure');
if (terrain.docker)
features.push('Docker container');
return features;
}
//# sourceMappingURL=detect-terrain.js.map