112 lines
4.2 KiB
JavaScript
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
|