Files
myeasycms-v2/apps/dev-tool/app/lib/prerequisites-dashboard.loader.ts
Giancarlo Buomprisco 7ebff31475 Next.js Supabase V3 (#463)
Version 3 of the kit:
- Radix UI replaced with Base UI (using the Shadcn UI patterns)
- next-intl replaces react-i18next
- enhanceAction deprecated; usage moved to next-safe-action
- main layout now wrapped with [locale] path segment
- Teams only mode
- Layout updates
- Zod v4
- Next.js 16.2
- Typescript 6
- All other dependencies updated
- Removed deprecated Edge CSRF
- Dynamic Github Action runner
2026-03-24 13:40:38 +08:00

155 lines
3.5 KiB
TypeScript

import {
type KitPrerequisitesDeps,
createKitPrerequisitesService,
} from '@kit/mcp-server/prerequisites';
import { execFile } from 'node:child_process';
import { access, readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { promisify } from 'node:util';
const execFileAsync = promisify(execFile);
export async function loadDashboardKitPrerequisites() {
const rootPath = await findWorkspaceRoot(process.cwd());
const service = createKitPrerequisitesService(
createKitPrerequisitesDeps(rootPath),
);
return service.check({});
}
function createKitPrerequisitesDeps(rootPath: string): KitPrerequisitesDeps {
return {
async getVariantFamily() {
const variant = await resolveVariant(rootPath);
return variant.includes('supabase') ? 'supabase' : 'orm';
},
async executeCommand(command: string, args: string[]) {
const result = await executeWithFallback(rootPath, command, args);
return {
stdout: result.stdout,
stderr: result.stderr,
exitCode: 0,
};
},
};
}
async function executeWithFallback(
rootPath: string,
command: string,
args: string[],
) {
try {
return await execFileAsync(command, args, {
cwd: rootPath,
});
} catch (error) {
if (isLocalCliCandidate(command)) {
const localBinCandidates = [
join(rootPath, 'node_modules', '.bin', command),
join(rootPath, 'apps', 'web', 'node_modules', '.bin', command),
];
for (const localBin of localBinCandidates) {
try {
return await execFileAsync(localBin, args, {
cwd: rootPath,
});
} catch {
// Try next local binary candidate.
}
}
try {
return await execFileAsync('pnpm', ['exec', command, ...args], {
cwd: rootPath,
});
} catch {
return execFileAsync(
'pnpm',
['--filter', 'web', 'exec', command, ...args],
{
cwd: rootPath,
},
);
}
}
throw error;
}
}
function isLocalCliCandidate(command: string) {
return command === 'supabase' || command === 'stripe';
}
async function resolveVariant(rootPath: string) {
const configPath = join(rootPath, '.makerkit', 'config.json');
try {
await access(configPath);
const config = JSON.parse(await readFile(configPath, 'utf8')) as Record<
string,
unknown
>;
const variant =
readString(config, 'variant') ??
readString(config, 'template') ??
readString(config, 'kitVariant');
if (variant) {
return variant;
}
} catch {
// Fall through to heuristic.
}
if (await pathExists(join(rootPath, 'apps', 'web', 'supabase'))) {
return 'next-supabase';
}
return 'next-drizzle';
}
function readString(obj: Record<string, unknown>, key: string) {
const value = obj[key];
return typeof value === 'string' && value.length > 0 ? value : null;
}
async function pathExists(path: string) {
try {
await access(path);
return true;
} catch {
return false;
}
}
async function findWorkspaceRoot(startPath: string) {
let current = startPath;
for (let depth = 0; depth < 6; depth++) {
const workspaceManifest = join(current, 'pnpm-workspace.yaml');
try {
await access(workspaceManifest);
return current;
} catch {
// Continue to parent path.
}
const parent = join(current, '..');
if (parent === current) {
break;
}
current = parent;
}
return startPath;
}