Files
2026-07-31 13:12:54 -04:00

112 lines
4.2 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
import { detectJsPackageManager, hasDependency, installCommandFor, readPackageJson, runScriptCommandFor, } from './packageManager.js';
function detectBundler(root, pkg) {
if (hasDependency(pkg, 'next') || fileExists(root, ['next.config.js', 'next.config.mjs', 'next.config.ts'])) {
return 'next';
}
if (fileExists(root, ['vite.config.js', 'vite.config.ts', 'vite.config.mjs']))
return 'vite';
if (hasDependency(pkg, 'react-scripts'))
return 'create-react-app';
return 'unknown';
}
function detectNextRouter(root) {
const appLayouts = [
path.join(root, 'app', 'layout.tsx'),
path.join(root, 'app', 'layout.jsx'),
path.join(root, 'src', 'app', 'layout.tsx'),
path.join(root, 'src', 'app', 'layout.jsx'),
];
if (appLayouts.some((p) => fs.existsSync(p)))
return 'app';
const pagesIndexes = [
path.join(root, 'pages', 'index.tsx'),
path.join(root, 'pages', 'index.jsx'),
path.join(root, 'pages', 'index.js'),
path.join(root, 'src', 'pages', 'index.tsx'),
path.join(root, 'src', 'pages', 'index.jsx'),
];
if (pagesIndexes.some((p) => fs.existsSync(p)))
return 'pages';
return 'unknown';
}
function fileExists(root, names) {
return names.some((name) => fs.existsSync(path.join(root, name)));
}
export class ReactAdapter {
root;
kind = 'react';
label;
packageManager;
pkg;
bundler;
nextRouter;
constructor(root) {
this.root = root;
this.pkg = readPackageJson(root);
this.packageManager = detectJsPackageManager(root);
this.bundler = detectBundler(root, this.pkg);
this.nextRouter = this.bundler === 'next' ? detectNextRouter(root) : 'unknown';
this.label = `React (${this.bundler})`;
}
installCommand() {
const { command, args } = installCommandFor(this.packageManager);
return { command, args, cwd: this.root };
}
devCommand() {
const script = this.findScript(['dev', 'start']);
if (!script) {
throw new Error(`No "dev" or "start" script found in package.json at "${this.root}".`);
}
const { command, args } = runScriptCommandFor(this.packageManager, script);
return { command, args, cwd: this.root };
}
buildCommand() {
return this.scriptCommandOrNull(['build']);
}
testCommand(extraArgs = []) {
return this.scriptCommandOrNull(['test'], extraArgs);
}
lintCommand(fix = false) {
return this.scriptCommandOrNull(fix ? ['format', 'lint:fix'] : ['lint']);
}
readyPattern() {
switch (this.bundler) {
case 'vite':
return /Local:\s+(https?:\/\/\S+)/i;
case 'next':
return /(?:Local|ready)[^:]*:\s*(https?:\/\/\S+)|started server on[^,]*,\s*url:\s*(https?:\/\/\S+)/i;
case 'create-react-app':
return /Local:\s+(https?:\/\/\S+)|Compiled successfully/i;
default:
return /(https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)[:\d]*\S*)/i;
}
}
describe() {
const isNext = this.bundler === 'next';
return {
kind: this.kind,
label: this.label,
root: this.root,
packageManager: this.packageManager,
bundler: this.bundler,
router: isNext ? this.nextRouter : null,
rsc: isNext && this.nextRouter === 'app',
reactVersion: this.pkg?.dependencies?.react ?? this.pkg?.devDependencies?.react ?? null,
typescript: hasDependency(this.pkg, 'typescript') || fileExists(this.root, ['tsconfig.json']),
scripts: this.pkg?.scripts ?? {},
};
}
findScript(candidates) {
return candidates.find((name) => this.pkg?.scripts?.[name]);
}
scriptCommandOrNull(candidates, extraArgs = []) {
const script = this.findScript(candidates);
if (!script)
return null;
const { command, args } = runScriptCommandFor(this.packageManager, script, extraArgs);
return { command, args, cwd: this.root };
}
}
//# sourceMappingURL=react.js.map