🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@contentrain/mcp

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@contentrain/mcp - npm Package Compare versions

Comparing version
2.3.0
to
2.3.1
+455
dist/detect-CdDU4Ew_.mjs
import { a as readJson, o as readText, r as pathExists } from "./fs-DLbVB-Ek.mjs";
import { dirname, join } from "node:path";
import { readdir } from "node:fs/promises";
//#region src/util/detect.ts
const STACK_META = {
nuxt: {
name: "Nuxt",
description: "Vue meta-framework with SSR/SSG"
},
next: {
name: "Next.js",
description: "React framework with SSR/SSG"
},
astro: {
name: "Astro",
description: "Content-first web framework"
},
sveltekit: {
name: "SvelteKit",
description: "Svelte meta-framework"
},
remix: {
name: "Remix",
description: "Full-stack React framework"
},
analog: {
name: "Analog",
description: "Angular meta-framework"
},
vue: {
name: "Vue",
description: "Progressive JavaScript framework"
},
react: {
name: "React",
description: "UI library for web and native"
},
svelte: {
name: "Svelte",
description: "Compile-time UI framework"
},
solid: {
name: "Solid",
description: "Reactive UI library"
},
angular: {
name: "Angular",
description: "Platform for web applications"
},
"react-native": {
name: "React Native",
description: "Cross-platform mobile framework"
},
expo: {
name: "Expo",
description: "React Native toolchain"
},
flutter: {
name: "Flutter",
description: "Cross-platform UI toolkit (Dart)"
},
node: {
name: "Node.js",
description: "Server-side JavaScript runtime"
},
express: {
name: "Express",
description: "Minimal Node.js web framework"
},
fastify: {
name: "Fastify",
description: "Fast Node.js web framework"
},
nestjs: {
name: "NestJS",
description: "Progressive Node.js framework"
},
django: {
name: "Django",
description: "Python web framework"
},
rails: {
name: "Rails",
description: "Ruby web framework"
},
laravel: {
name: "Laravel",
description: "PHP web framework"
},
go: {
name: "Go",
description: "Go programming language"
},
rust: {
name: "Rust",
description: "Rust programming language"
},
dotnet: {
name: ".NET",
description: "Microsoft .NET platform"
},
hugo: {
name: "Hugo",
description: "Go-based static site generator"
},
jekyll: {
name: "Jekyll",
description: "Ruby-based static site generator"
},
eleventy: {
name: "Eleventy",
description: "JavaScript static site generator"
},
electron: {
name: "Electron",
description: "Cross-platform desktop apps"
},
tauri: {
name: "Tauri",
description: "Lightweight desktop apps (Rust)"
},
other: {
name: "Other",
description: "Unknown or unsupported framework"
}
};
function inferPlatform(stack) {
switch (stack) {
case "nuxt":
case "next":
case "astro":
case "sveltekit":
case "remix":
case "analog":
case "vue":
case "react":
case "svelte":
case "solid":
case "angular": return "web";
case "react-native":
case "expo":
case "flutter": return "mobile";
case "node":
case "express":
case "fastify":
case "nestjs":
case "django":
case "rails":
case "laravel":
case "go":
case "rust":
case "dotnet": return "api";
case "electron":
case "tauri": return "desktop";
case "hugo":
case "jekyll":
case "eleventy": return "static";
default: return "other";
}
}
/** Backward-compatible: returns just the StackType string */
async function detectStack(projectRoot) {
return (await detectStackInfo(projectRoot)).stack;
}
/** Rich detection: stack + platform + monorepo + features */
async function detectStackInfo(projectRoot) {
let stack = await detectFromDeps(projectRoot);
if (stack === "other") {
let dir = dirname(projectRoot);
for (let i = 0; i < 5; i++) {
if ((await readJson(join(dir, "package.json")))?.workspaces) {
const rootStack = await detectFromDeps(dir);
if (rootStack !== "other") {
stack = rootStack;
break;
}
}
const parent = dirname(dir);
if (parent === dir) break;
dir = parent;
}
}
if (stack === "other") stack = await detectFromWorkspaces(projectRoot);
if (stack === "other") stack = await detectNonJs(projectRoot);
const mono = await detectMonorepo(projectRoot);
const features = await detectFeatures(projectRoot);
const meta = STACK_META[stack];
return {
stack,
platform: inferPlatform(stack),
name: meta.name,
description: meta.description,
monorepo: mono.isMonorepo,
monorepoTool: mono.tool,
features
};
}
async function detectFromDeps(dir) {
const pkg = await readJson(join(dir, "package.json"));
if (!pkg) return "other";
const allDeps = {
...pkg.dependencies,
...pkg.devDependencies
};
if ("nuxt" in allDeps) return "nuxt";
if ("next" in allDeps) return "next";
if ("astro" in allDeps) return "astro";
if ("@sveltejs/kit" in allDeps) return "sveltekit";
if ("@remix-run/node" in allDeps || "remix" in allDeps) return "remix";
if ("@analogjs/platform" in allDeps) return "analog";
if ("expo" in allDeps) return "expo";
if ("react-native" in allDeps) return "react-native";
if ("vue" in allDeps) return "vue";
if ("svelte" in allDeps) return "svelte";
if ("solid-js" in allDeps) return "solid";
if ("@angular/core" in allDeps) return "angular";
if ("react" in allDeps) return "react";
if ("electron" in allDeps) return "electron";
if ("@tauri-apps/api" in allDeps) return "tauri";
if ("@nestjs/core" in allDeps) return "nestjs";
if ("fastify" in allDeps) return "fastify";
if ("express" in allDeps) return "express";
if ("@11ty/eleventy" in allDeps) return "eleventy";
if ("node" in (pkg.dependencies ?? {})) return "node";
return "other";
}
/**
* Ranked by how strongly the stack determines content conventions — a
* meta-framework dictates the i18n setup and the replacement expression,
* a plain UI library does not. Frequency would be the wrong tie-break: a
* monorepo with one Next app and two React packages is a Next project.
*/
const STACK_PRIORITY = [
"nuxt",
"next",
"astro",
"sveltekit",
"remix",
"analog",
"expo",
"react-native",
"vue",
"react",
"svelte",
"solid",
"angular",
"electron",
"tauri",
"nestjs",
"fastify",
"express",
"eleventy",
"node"
];
/** Cap the fan-out so a large monorepo cannot turn detection into a crawl. */
const MAX_WORKSPACE_DIRS = 50;
/**
* Workspace globs from `pnpm-workspace.yaml` or the `workspaces` field.
*
* The pnpm file is parsed line by line rather than with a YAML library: we
* need exactly one list of strings with a fixed shape, and a parser
* dependency for that is not worth the install size.
*/
async function readWorkspacePatterns(root) {
const pnpm = await readText(join(root, "pnpm-workspace.yaml"));
if (pnpm) {
const patterns = [];
let inPackages = false;
for (const raw of pnpm.split("\n")) {
const line = raw.replace(/#.*$/, "").trimEnd();
if (/^packages:\s*$/.test(line)) {
inPackages = true;
continue;
}
if (!inPackages) continue;
const item = /^\s+-\s*['"]?([^'"]+?)['"]?\s*$/.exec(line);
if (item?.[1]) {
patterns.push(item[1]);
continue;
}
if (line.trim() !== "") break;
}
if (patterns.length > 0) return patterns;
}
const ws = (await readJson(join(root, "package.json")))?.workspaces;
if (Array.isArray(ws)) return ws;
if (ws && Array.isArray(ws.packages)) return ws.packages;
return [];
}
/**
* Expand workspace patterns to directories. Only a trailing `*` segment is
* expanded — that covers `apps/*` and `packages/*`, which is what real
* workspace files use. Deeper globs and negations are skipped rather than
* half-supported.
*/
async function listWorkspaceDirs(root) {
const patterns = (await readWorkspacePatterns(root)).filter((p) => !p.startsWith("!"));
return (await Promise.all(patterns.map(async (pattern) => {
const star = pattern.indexOf("*");
if (star === -1) return [join(root, pattern)];
const base = join(root, pattern.slice(0, star).replace(/\/+$/, ""));
try {
return (await readdir(base, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => join(base, e.name));
} catch {
return [];
}
}))).flat().slice(0, MAX_WORKSPACE_DIRS);
}
/** Highest-priority stack across the workspace packages. */
async function detectFromWorkspaces(root) {
const dirs = await listWorkspaceDirs(root);
if (dirs.length === 0) return "other";
const found = new Set(await Promise.all(dirs.map((d) => detectFromDeps(d))));
return STACK_PRIORITY.find((s) => found.has(s)) ?? "other";
}
async function detectNonJs(projectRoot) {
if (await pathExists(join(projectRoot, "go.mod"))) return "go";
if (await pathExists(join(projectRoot, "Cargo.toml"))) return "rust";
for (const f of [
"requirements.txt",
"pyproject.toml",
"setup.py"
]) if (await pathExists(join(projectRoot, f))) {
try {
const content = await readText(join(projectRoot, f));
if (content?.includes("django") || content?.includes("Django")) return "django";
} catch {}
return "other";
}
if (await pathExists(join(projectRoot, "Gemfile"))) {
try {
if ((await readText(join(projectRoot, "Gemfile")))?.includes("rails")) return "rails";
} catch {}
return "other";
}
for (const pattern of ["*.csproj", "*.sln"]) {
if (pattern === "*.csproj" && await pathExists(join(projectRoot, "Program.cs"))) return "dotnet";
if (pattern === "*.sln" && await pathExists(join(projectRoot, "Program.cs"))) return "dotnet";
}
if (await pathExists(join(projectRoot, "composer.json"))) {
try {
if (((await readJson(join(projectRoot, "composer.json")))?.require)?.["laravel/framework"]) return "laravel";
} catch {}
return "other";
}
if (await pathExists(join(projectRoot, "pubspec.yaml"))) return "flutter";
if (await pathExists(join(projectRoot, "hugo.toml"))) return "hugo";
if (await pathExists(join(projectRoot, "config.toml"))) try {
const content = await readText(join(projectRoot, "config.toml"));
if (content?.includes("baseURL") || content?.includes("hugo")) return "hugo";
} catch {}
if (await pathExists(join(projectRoot, "_config.yml"))) return "jekyll";
return "other";
}
async function detectMonorepo(projectRoot) {
if (await pathExists(join(projectRoot, "pnpm-workspace.yaml"))) return {
isMonorepo: true,
tool: "pnpm workspaces"
};
if (await pathExists(join(projectRoot, "lerna.json"))) return {
isMonorepo: true,
tool: "Lerna"
};
if (await pathExists(join(projectRoot, "nx.json"))) return {
isMonorepo: true,
tool: "Nx"
};
if (await pathExists(join(projectRoot, "turbo.json"))) return {
isMonorepo: true,
tool: "Turborepo"
};
if ((await readJson(join(projectRoot, "package.json")))?.workspaces) return {
isMonorepo: true,
tool: "npm/yarn workspaces"
};
let dir = dirname(projectRoot);
for (let i = 0; i < 5; i++) {
if (await pathExists(join(dir, "pnpm-workspace.yaml"))) return {
isMonorepo: true,
tool: "pnpm workspaces"
};
if (await pathExists(join(dir, "lerna.json"))) return {
isMonorepo: true,
tool: "Lerna"
};
if (await pathExists(join(dir, "nx.json"))) return {
isMonorepo: true,
tool: "Nx"
};
if (await pathExists(join(dir, "turbo.json"))) return {
isMonorepo: true,
tool: "Turborepo"
};
if ((await readJson(join(dir, "package.json")))?.workspaces) return {
isMonorepo: true,
tool: "npm/yarn workspaces"
};
const parent = dirname(dir);
if (parent === dir) break;
dir = parent;
}
return { isMonorepo: false };
}
async function detectFeatures(projectRoot) {
const features = [];
const allDeps = await collectAllDeps(projectRoot);
if ("typescript" in allDeps || await pathExists(join(projectRoot, "tsconfig.json"))) features.push("TypeScript");
for (const lib of [
"vue-i18n",
"@nuxtjs/i18n",
"next-intl",
"react-intl",
"i18next",
"react-i18next"
]) if (lib in allDeps) {
features.push(`i18n (${lib})`);
break;
}
if ("tailwindcss" in allDeps) features.push("Tailwind CSS");
else if ("sass" in allDeps || "scss" in allDeps) features.push("Sass/SCSS");
if ("vitest" in allDeps) features.push("Vitest");
else if ("jest" in allDeps) features.push("Jest");
if ("pinia" in allDeps) features.push("Pinia");
else if ("zustand" in allDeps) features.push("Zustand");
else if ("@reduxjs/toolkit" in allDeps || "redux" in allDeps) features.push("Redux");
return features;
}
async function collectAllDeps(projectRoot) {
const pkg = await readJson(join(projectRoot, "package.json"));
const allDeps = {
...pkg?.dependencies,
...pkg?.devDependencies
};
let dir = dirname(projectRoot);
for (let i = 0; i < 5; i++) {
const parentPkg = await readJson(join(dir, "package.json"));
if (parentPkg?.workspaces) {
Object.assign(allDeps, parentPkg.dependencies, parentPkg.devDependencies);
break;
}
const parent = dirname(dir);
if (parent === dir) break;
dir = parent;
}
const workspaceDirs = await listWorkspaceDirs(projectRoot);
if (workspaceDirs.length > 0) {
const pkgs = await Promise.all(workspaceDirs.map((d) => readJson(join(d, "package.json"))));
for (const wsPkg of pkgs) if (wsPkg) Object.assign(allDeps, wsPkg.dependencies, wsPkg.devDependencies);
}
return allDeps;
}
//#endregion
export { detectStackInfo as n, inferPlatform as r, detectStack as t };
//# sourceMappingURL=detect-CdDU4Ew_.mjs.map
{"version":3,"file":"detect-CdDU4Ew_.mjs","names":[],"sources":["../src/util/detect.ts"],"sourcesContent":["import type { StackType, Platform } from '@contentrain/types'\nimport { join, dirname } from 'node:path'\nimport { readdir } from 'node:fs/promises'\nimport { readJson, pathExists, readText } from './fs.js'\n\ninterface PackageJson {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n workspaces?: string[] | { packages: string[] }\n}\n\nexport interface StackInfo {\n stack: StackType\n platform: Platform\n name: string\n description: string\n monorepo: boolean\n monorepoTool?: string\n features: string[]\n}\n\nconst STACK_META: Record<StackType, { name: string; description: string }> = {\n // Meta-frameworks\n nuxt: { name: 'Nuxt', description: 'Vue meta-framework with SSR/SSG' },\n next: { name: 'Next.js', description: 'React framework with SSR/SSG' },\n astro: { name: 'Astro', description: 'Content-first web framework' },\n sveltekit: { name: 'SvelteKit', description: 'Svelte meta-framework' },\n remix: { name: 'Remix', description: 'Full-stack React framework' },\n analog: { name: 'Analog', description: 'Angular meta-framework' },\n // Plain frameworks\n vue: { name: 'Vue', description: 'Progressive JavaScript framework' },\n react: { name: 'React', description: 'UI library for web and native' },\n svelte: { name: 'Svelte', description: 'Compile-time UI framework' },\n solid: { name: 'Solid', description: 'Reactive UI library' },\n angular: { name: 'Angular', description: 'Platform for web applications' },\n // Mobile\n 'react-native': { name: 'React Native', description: 'Cross-platform mobile framework' },\n expo: { name: 'Expo', description: 'React Native toolchain' },\n flutter: { name: 'Flutter', description: 'Cross-platform UI toolkit (Dart)' },\n // Backend\n node: { name: 'Node.js', description: 'Server-side JavaScript runtime' },\n express: { name: 'Express', description: 'Minimal Node.js web framework' },\n fastify: { name: 'Fastify', description: 'Fast Node.js web framework' },\n nestjs: { name: 'NestJS', description: 'Progressive Node.js framework' },\n django: { name: 'Django', description: 'Python web framework' },\n rails: { name: 'Rails', description: 'Ruby web framework' },\n laravel: { name: 'Laravel', description: 'PHP web framework' },\n go: { name: 'Go', description: 'Go programming language' },\n rust: { name: 'Rust', description: 'Rust programming language' },\n dotnet: { name: '.NET', description: 'Microsoft .NET platform' },\n // Static\n hugo: { name: 'Hugo', description: 'Go-based static site generator' },\n jekyll: { name: 'Jekyll', description: 'Ruby-based static site generator' },\n eleventy: { name: 'Eleventy', description: 'JavaScript static site generator' },\n // Desktop\n electron: { name: 'Electron', description: 'Cross-platform desktop apps' },\n tauri: { name: 'Tauri', description: 'Lightweight desktop apps (Rust)' },\n // Catch-all\n other: { name: 'Other', description: 'Unknown or unsupported framework' },\n}\n\nexport function inferPlatform(stack: StackType): Platform {\n switch (stack) {\n case 'nuxt': case 'next': case 'astro': case 'sveltekit': case 'remix': case 'analog':\n case 'vue': case 'react': case 'svelte': case 'solid': case 'angular':\n return 'web'\n case 'react-native': case 'expo': case 'flutter':\n return 'mobile'\n case 'node': case 'express': case 'fastify': case 'nestjs':\n case 'django': case 'rails': case 'laravel': case 'go': case 'rust': case 'dotnet':\n return 'api'\n case 'electron': case 'tauri':\n return 'desktop'\n case 'hugo': case 'jekyll': case 'eleventy':\n return 'static'\n default:\n return 'other'\n }\n}\n\n/** Backward-compatible: returns just the StackType string */\nexport async function detectStack(projectRoot: string): Promise<StackType> {\n const info = await detectStackInfo(projectRoot)\n return info.stack\n}\n\n/** Rich detection: stack + platform + monorepo + features */\nexport async function detectStackInfo(projectRoot: string): Promise<StackInfo> {\n let stack = await detectFromDeps(projectRoot)\n\n // Walk up for monorepo root if local didn't match\n if (stack === 'other') {\n let dir = dirname(projectRoot)\n for (let i = 0; i < 5; i++) {\n const pkg = await readJson<PackageJson>(join(dir, 'package.json'))\n if (pkg?.workspaces) {\n const rootStack = await detectFromDeps(dir)\n if (rootStack !== 'other') {\n stack = rootStack\n break\n }\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n }\n\n // Look DOWN into the workspaces. The walk above handles \"I am inside a\n // package, find the monorepo root\"; this handles the opposite and more\n // common case — projectRoot IS the monorepo root, whose package.json holds\n // only tooling (turbo, nx) while the frameworks live in apps/* and\n // packages/*. Without this a Next.js monorepo reports `other`, and the\n // stack is what picks the replacement conventions during normalize.\n if (stack === 'other') {\n stack = await detectFromWorkspaces(projectRoot)\n }\n\n // Non-JS detection if still 'other'\n if (stack === 'other') {\n stack = await detectNonJs(projectRoot)\n }\n\n const mono = await detectMonorepo(projectRoot)\n const features = await detectFeatures(projectRoot)\n const meta = STACK_META[stack]\n\n return {\n stack,\n platform: inferPlatform(stack),\n name: meta.name,\n description: meta.description,\n monorepo: mono.isMonorepo,\n monorepoTool: mono.tool,\n features,\n }\n}\n\nasync function detectFromDeps(dir: string): Promise<StackType> {\n const pkg = await readJson<PackageJson>(join(dir, 'package.json'))\n if (!pkg) return 'other'\n\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\n // Meta-frameworks (check BEFORE plain frameworks)\n if ('nuxt' in allDeps) return 'nuxt'\n if ('next' in allDeps) return 'next'\n if ('astro' in allDeps) return 'astro'\n if ('@sveltejs/kit' in allDeps) return 'sveltekit'\n if ('@remix-run/node' in allDeps || 'remix' in allDeps) return 'remix'\n if ('@analogjs/platform' in allDeps) return 'analog'\n\n // Mobile (check before plain react)\n if ('expo' in allDeps) return 'expo'\n if ('react-native' in allDeps) return 'react-native'\n\n // Plain frameworks\n if ('vue' in allDeps) return 'vue'\n if ('svelte' in allDeps) return 'svelte'\n if ('solid-js' in allDeps) return 'solid'\n if ('@angular/core' in allDeps) return 'angular'\n if ('react' in allDeps) return 'react'\n\n // Desktop\n if ('electron' in allDeps) return 'electron'\n if ('@tauri-apps/api' in allDeps) return 'tauri'\n\n // Backend (Node.js)\n if ('@nestjs/core' in allDeps) return 'nestjs'\n if ('fastify' in allDeps) return 'fastify'\n if ('express' in allDeps) return 'express'\n\n // Static site generators\n if ('@11ty/eleventy' in allDeps) return 'eleventy'\n\n // Generic Node.js (has package.json but no framework)\n if ('node' in (pkg.dependencies ?? {})) return 'node'\n\n return 'other'\n}\n\n/**\n * Ranked by how strongly the stack determines content conventions — a\n * meta-framework dictates the i18n setup and the replacement expression,\n * a plain UI library does not. Frequency would be the wrong tie-break: a\n * monorepo with one Next app and two React packages is a Next project.\n */\nconst STACK_PRIORITY: readonly StackType[] = [\n 'nuxt', 'next', 'astro', 'sveltekit', 'remix', 'analog',\n 'expo', 'react-native',\n 'vue', 'react', 'svelte', 'solid', 'angular',\n 'electron', 'tauri',\n 'nestjs', 'fastify', 'express',\n 'eleventy', 'node',\n]\n\n/** Cap the fan-out so a large monorepo cannot turn detection into a crawl. */\nconst MAX_WORKSPACE_DIRS = 50\n\n/**\n * Workspace globs from `pnpm-workspace.yaml` or the `workspaces` field.\n *\n * The pnpm file is parsed line by line rather than with a YAML library: we\n * need exactly one list of strings with a fixed shape, and a parser\n * dependency for that is not worth the install size.\n */\nasync function readWorkspacePatterns(root: string): Promise<string[]> {\n const pnpm = await readText(join(root, 'pnpm-workspace.yaml'))\n if (pnpm) {\n const patterns: string[] = []\n let inPackages = false\n for (const raw of pnpm.split('\\n')) {\n const line = raw.replace(/#.*$/, '').trimEnd()\n if (/^packages:\\s*$/.test(line)) { inPackages = true; continue }\n if (!inPackages) continue\n const item = /^\\s+-\\s*['\"]?([^'\"]+?)['\"]?\\s*$/.exec(line)\n if (item?.[1]) { patterns.push(item[1]); continue }\n if (line.trim() !== '') break // a new top-level key ends the list\n }\n if (patterns.length > 0) return patterns\n }\n\n const pkg = await readJson<PackageJson>(join(root, 'package.json'))\n const ws = pkg?.workspaces\n if (Array.isArray(ws)) return ws\n if (ws && Array.isArray(ws.packages)) return ws.packages\n return []\n}\n\n/**\n * Expand workspace patterns to directories. Only a trailing `*` segment is\n * expanded — that covers `apps/*` and `packages/*`, which is what real\n * workspace files use. Deeper globs and negations are skipped rather than\n * half-supported.\n */\nasync function listWorkspaceDirs(root: string): Promise<string[]> {\n const patterns = (await readWorkspacePatterns(root)).filter(p => !p.startsWith('!'))\n\n const expanded = await Promise.all(patterns.map(async (pattern) => {\n const star = pattern.indexOf('*')\n if (star === -1) return [join(root, pattern)]\n const base = join(root, pattern.slice(0, star).replace(/\\/+$/, ''))\n try {\n const entries = await readdir(base, { withFileTypes: true })\n return entries.filter(e => e.isDirectory()).map(e => join(base, e.name))\n } catch {\n return []\n }\n }))\n\n return expanded.flat().slice(0, MAX_WORKSPACE_DIRS)\n}\n\n/** Highest-priority stack across the workspace packages. */\nasync function detectFromWorkspaces(root: string): Promise<StackType> {\n const dirs = await listWorkspaceDirs(root)\n if (dirs.length === 0) return 'other'\n\n const found = new Set(await Promise.all(dirs.map(d => detectFromDeps(d))))\n return STACK_PRIORITY.find(s => found.has(s)) ?? 'other'\n}\n\nasync function detectNonJs(projectRoot: string): Promise<StackType> {\n // Go\n if (await pathExists(join(projectRoot, 'go.mod'))) return 'go'\n\n // Rust\n if (await pathExists(join(projectRoot, 'Cargo.toml'))) return 'rust'\n\n // Python\n for (const f of ['requirements.txt', 'pyproject.toml', 'setup.py']) {\n if (await pathExists(join(projectRoot, f))) {\n // Check for Django\n try {\n const content = await readText(join(projectRoot, f))\n if (content?.includes('django') || content?.includes('Django')) return 'django'\n } catch { /* ignore */ }\n return 'other' // Generic Python — no specific stack type yet\n }\n }\n\n // Ruby\n if (await pathExists(join(projectRoot, 'Gemfile'))) {\n try {\n const content = await readText(join(projectRoot, 'Gemfile'))\n if (content?.includes('rails')) return 'rails'\n } catch { /* ignore */ }\n return 'other'\n }\n\n // .NET\n const dotnetFiles = ['*.csproj', '*.sln']\n for (const pattern of dotnetFiles) {\n if (pattern === '*.csproj' && await pathExists(join(projectRoot, 'Program.cs'))) return 'dotnet'\n if (pattern === '*.sln' && await pathExists(join(projectRoot, 'Program.cs'))) return 'dotnet'\n }\n\n // PHP / Laravel\n if (await pathExists(join(projectRoot, 'composer.json'))) {\n try {\n const composer = await readJson<Record<string, unknown>>(join(projectRoot, 'composer.json'))\n const require = composer?.require as Record<string, string> | undefined\n if (require?.['laravel/framework']) return 'laravel'\n } catch { /* ignore */ }\n return 'other'\n }\n\n // Flutter (Dart)\n if (await pathExists(join(projectRoot, 'pubspec.yaml'))) return 'flutter'\n\n // Hugo\n if (await pathExists(join(projectRoot, 'hugo.toml'))) return 'hugo'\n if (await pathExists(join(projectRoot, 'config.toml'))) {\n try {\n const content = await readText(join(projectRoot, 'config.toml'))\n if (content?.includes('baseURL') || content?.includes('hugo')) return 'hugo'\n } catch { /* ignore */ }\n }\n\n // Jekyll\n if (await pathExists(join(projectRoot, '_config.yml'))) return 'jekyll'\n\n return 'other'\n}\n\nasync function detectMonorepo(projectRoot: string): Promise<{ isMonorepo: boolean; tool?: string }> {\n if (await pathExists(join(projectRoot, 'pnpm-workspace.yaml'))) return { isMonorepo: true, tool: 'pnpm workspaces' }\n if (await pathExists(join(projectRoot, 'lerna.json'))) return { isMonorepo: true, tool: 'Lerna' }\n if (await pathExists(join(projectRoot, 'nx.json'))) return { isMonorepo: true, tool: 'Nx' }\n if (await pathExists(join(projectRoot, 'turbo.json'))) return { isMonorepo: true, tool: 'Turborepo' }\n\n const pkg = await readJson<PackageJson>(join(projectRoot, 'package.json'))\n if (pkg?.workspaces) return { isMonorepo: true, tool: 'npm/yarn workspaces' }\n\n // Walk up\n let dir = dirname(projectRoot)\n for (let i = 0; i < 5; i++) {\n if (await pathExists(join(dir, 'pnpm-workspace.yaml'))) return { isMonorepo: true, tool: 'pnpm workspaces' }\n if (await pathExists(join(dir, 'lerna.json'))) return { isMonorepo: true, tool: 'Lerna' }\n if (await pathExists(join(dir, 'nx.json'))) return { isMonorepo: true, tool: 'Nx' }\n if (await pathExists(join(dir, 'turbo.json'))) return { isMonorepo: true, tool: 'Turborepo' }\n const parentPkg = await readJson<PackageJson>(join(dir, 'package.json'))\n if (parentPkg?.workspaces) return { isMonorepo: true, tool: 'npm/yarn workspaces' }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n\n return { isMonorepo: false }\n}\n\nasync function detectFeatures(projectRoot: string): Promise<string[]> {\n const features: string[] = []\n const allDeps = await collectAllDeps(projectRoot)\n\n if ('typescript' in allDeps || await pathExists(join(projectRoot, 'tsconfig.json'))) features.push('TypeScript')\n\n const i18nLibs = ['vue-i18n', '@nuxtjs/i18n', 'next-intl', 'react-intl', 'i18next', 'react-i18next']\n for (const lib of i18nLibs) {\n if (lib in allDeps) { features.push(`i18n (${lib})`); break }\n }\n\n if ('tailwindcss' in allDeps) features.push('Tailwind CSS')\n else if ('sass' in allDeps || 'scss' in allDeps) features.push('Sass/SCSS')\n\n if ('vitest' in allDeps) features.push('Vitest')\n else if ('jest' in allDeps) features.push('Jest')\n\n if ('pinia' in allDeps) features.push('Pinia')\n else if ('zustand' in allDeps) features.push('Zustand')\n else if ('@reduxjs/toolkit' in allDeps || 'redux' in allDeps) features.push('Redux')\n\n return features\n}\n\nasync function collectAllDeps(projectRoot: string): Promise<Record<string, string>> {\n const pkg = await readJson<PackageJson>(join(projectRoot, 'package.json'))\n const allDeps: Record<string, string> = { ...pkg?.dependencies, ...pkg?.devDependencies }\n\n let dir = dirname(projectRoot)\n for (let i = 0; i < 5; i++) {\n const parentPkg = await readJson<PackageJson>(join(dir, 'package.json'))\n if (parentPkg?.workspaces) {\n Object.assign(allDeps, parentPkg.dependencies, parentPkg.devDependencies)\n break\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n\n // Same blind spot as stack detection: at a monorepo root the interesting\n // dependencies (the i18n library above all) sit in the workspace packages,\n // not in the root manifest. Reporting \"no i18n\" for a project that has it\n // would send normalize down the wrong path.\n const workspaceDirs = await listWorkspaceDirs(projectRoot)\n if (workspaceDirs.length > 0) {\n const pkgs = await Promise.all(\n workspaceDirs.map(d => readJson<PackageJson>(join(d, 'package.json'))),\n )\n for (const wsPkg of pkgs) {\n if (wsPkg) Object.assign(allDeps, wsPkg.dependencies, wsPkg.devDependencies)\n }\n }\n\n return allDeps\n}\n"],"mappings":"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"}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

+2
-2

@@ -14,5 +14,5 @@ #!/usr/bin/env node

import "./local-8dJ5NIWL.mjs";
import "./detect-wVSI9VuY.mjs";
import "./detect-CdDU4Ew_.mjs";
import "./annotations-D3tlsF38.mjs";
import { n as createServer } from "./server-I6sqKvO8.mjs";
import { n as createServer } from "./server-CUZnuHQx.mjs";
import "./validator-ChiYk6ap.mjs";

@@ -19,0 +19,0 @@ import "./scan-config-BlNLRCMx.mjs";

@@ -13,5 +13,5 @@ import "./contracts-DfL0BfrD.mjs";

import "./local-8dJ5NIWL.mjs";
import "./detect-wVSI9VuY.mjs";
import "./detect-CdDU4Ew_.mjs";
import "./annotations-D3tlsF38.mjs";
import { n as createServer, t as DEFAULT_INSTRUCTIONS } from "./server-I6sqKvO8.mjs";
import { n as createServer, t as DEFAULT_INSTRUCTIONS } from "./server-CUZnuHQx.mjs";
import "./validator-ChiYk6ap.mjs";

@@ -18,0 +18,0 @@ import "./scan-config-BlNLRCMx.mjs";

@@ -13,5 +13,5 @@ import "../../contracts-DfL0BfrD.mjs";

import "../../local-8dJ5NIWL.mjs";
import "../../detect-wVSI9VuY.mjs";
import "../../detect-CdDU4Ew_.mjs";
import "../../annotations-D3tlsF38.mjs";
import { n as createServer } from "../../server-I6sqKvO8.mjs";
import { n as createServer } from "../../server-CUZnuHQx.mjs";
import "../../validator-ChiYk6ap.mjs";

@@ -18,0 +18,0 @@ import "../../scan-config-BlNLRCMx.mjs";

@@ -1,1 +0,1 @@

{"version":3,"file":"detect.d.mts","names":[],"sources":["../../src/util/detect.ts"],"mappings":";;;UAUiB,SAAA;EACf,KAAA,EAAO,SAAA;EACP,QAAA,EAAU,QAAA;EACV,IAAA;EACA,WAAA;EACA,QAAA;EACA,YAAA;EACA,QAAA;AAAA;AAAA,iBA2Cc,aAAA,CAAc,KAAA,EAAO,SAAA,GAAY,QAAA;;iBAoB3B,WAAA,CAAY,WAAA,WAAsB,OAAA,CAAQ,SAAA;;iBAM1C,eAAA,CAAgB,WAAA,WAAsB,OAAA,CAAQ,SAAA"}
{"version":3,"file":"detect.d.mts","names":[],"sources":["../../src/util/detect.ts"],"mappings":";;;UAWiB,SAAA;EACf,KAAA,EAAO,SAAA;EACP,QAAA,EAAU,QAAA;EACV,IAAA;EACA,WAAA;EACA,QAAA;EACA,YAAA;EACA,QAAA;AAAA;AAAA,iBA2Cc,aAAA,CAAc,KAAA,EAAO,SAAA,GAAY,QAAA;;iBAoB3B,WAAA,CAAY,WAAA,WAAsB,OAAA,CAAQ,SAAA;;iBAM1C,eAAA,CAAgB,WAAA,WAAsB,OAAA,CAAQ,SAAA"}
import "../fs-DLbVB-Ek.mjs";
import { n as detectStackInfo, r as inferPlatform, t as detectStack } from "../detect-wVSI9VuY.mjs";
import { n as detectStackInfo, r as inferPlatform, t as detectStack } from "../detect-CdDU4Ew_.mjs";
export { detectStack, detectStackInfo, inferPlatform };
{
"name": "@contentrain/mcp",
"version": "2.3.0",
"version": "2.3.1",
"mcpName": "io.github.Contentrain/contentrain",

@@ -5,0 +5,0 @@ "license": "MIT",

import { a as readJson, r as pathExists } from "./fs-DLbVB-Ek.mjs";
import { dirname, join } from "node:path";
//#region src/util/detect.ts
const STACK_META = {
nuxt: {
name: "Nuxt",
description: "Vue meta-framework with SSR/SSG"
},
next: {
name: "Next.js",
description: "React framework with SSR/SSG"
},
astro: {
name: "Astro",
description: "Content-first web framework"
},
sveltekit: {
name: "SvelteKit",
description: "Svelte meta-framework"
},
remix: {
name: "Remix",
description: "Full-stack React framework"
},
analog: {
name: "Analog",
description: "Angular meta-framework"
},
vue: {
name: "Vue",
description: "Progressive JavaScript framework"
},
react: {
name: "React",
description: "UI library for web and native"
},
svelte: {
name: "Svelte",
description: "Compile-time UI framework"
},
solid: {
name: "Solid",
description: "Reactive UI library"
},
angular: {
name: "Angular",
description: "Platform for web applications"
},
"react-native": {
name: "React Native",
description: "Cross-platform mobile framework"
},
expo: {
name: "Expo",
description: "React Native toolchain"
},
flutter: {
name: "Flutter",
description: "Cross-platform UI toolkit (Dart)"
},
node: {
name: "Node.js",
description: "Server-side JavaScript runtime"
},
express: {
name: "Express",
description: "Minimal Node.js web framework"
},
fastify: {
name: "Fastify",
description: "Fast Node.js web framework"
},
nestjs: {
name: "NestJS",
description: "Progressive Node.js framework"
},
django: {
name: "Django",
description: "Python web framework"
},
rails: {
name: "Rails",
description: "Ruby web framework"
},
laravel: {
name: "Laravel",
description: "PHP web framework"
},
go: {
name: "Go",
description: "Go programming language"
},
rust: {
name: "Rust",
description: "Rust programming language"
},
dotnet: {
name: ".NET",
description: "Microsoft .NET platform"
},
hugo: {
name: "Hugo",
description: "Go-based static site generator"
},
jekyll: {
name: "Jekyll",
description: "Ruby-based static site generator"
},
eleventy: {
name: "Eleventy",
description: "JavaScript static site generator"
},
electron: {
name: "Electron",
description: "Cross-platform desktop apps"
},
tauri: {
name: "Tauri",
description: "Lightweight desktop apps (Rust)"
},
other: {
name: "Other",
description: "Unknown or unsupported framework"
}
};
function inferPlatform(stack) {
switch (stack) {
case "nuxt":
case "next":
case "astro":
case "sveltekit":
case "remix":
case "analog":
case "vue":
case "react":
case "svelte":
case "solid":
case "angular": return "web";
case "react-native":
case "expo":
case "flutter": return "mobile";
case "node":
case "express":
case "fastify":
case "nestjs":
case "django":
case "rails":
case "laravel":
case "go":
case "rust":
case "dotnet": return "api";
case "electron":
case "tauri": return "desktop";
case "hugo":
case "jekyll":
case "eleventy": return "static";
default: return "other";
}
}
/** Backward-compatible: returns just the StackType string */
async function detectStack(projectRoot) {
return (await detectStackInfo(projectRoot)).stack;
}
/** Rich detection: stack + platform + monorepo + features */
async function detectStackInfo(projectRoot) {
let stack = await detectFromDeps(projectRoot);
if (stack === "other") {
let dir = dirname(projectRoot);
for (let i = 0; i < 5; i++) {
if ((await readJson(join(dir, "package.json")))?.workspaces) {
const rootStack = await detectFromDeps(dir);
if (rootStack !== "other") {
stack = rootStack;
break;
}
}
const parent = dirname(dir);
if (parent === dir) break;
dir = parent;
}
}
if (stack === "other") stack = await detectNonJs(projectRoot);
const mono = await detectMonorepo(projectRoot);
const features = await detectFeatures(projectRoot);
const meta = STACK_META[stack];
return {
stack,
platform: inferPlatform(stack),
name: meta.name,
description: meta.description,
monorepo: mono.isMonorepo,
monorepoTool: mono.tool,
features
};
}
async function detectFromDeps(dir) {
const pkg = await readJson(join(dir, "package.json"));
if (!pkg) return "other";
const allDeps = {
...pkg.dependencies,
...pkg.devDependencies
};
if ("nuxt" in allDeps) return "nuxt";
if ("next" in allDeps) return "next";
if ("astro" in allDeps) return "astro";
if ("@sveltejs/kit" in allDeps) return "sveltekit";
if ("@remix-run/node" in allDeps || "remix" in allDeps) return "remix";
if ("@analogjs/platform" in allDeps) return "analog";
if ("expo" in allDeps) return "expo";
if ("react-native" in allDeps) return "react-native";
if ("vue" in allDeps) return "vue";
if ("svelte" in allDeps) return "svelte";
if ("solid-js" in allDeps) return "solid";
if ("@angular/core" in allDeps) return "angular";
if ("react" in allDeps) return "react";
if ("electron" in allDeps) return "electron";
if ("@tauri-apps/api" in allDeps) return "tauri";
if ("@nestjs/core" in allDeps) return "nestjs";
if ("fastify" in allDeps) return "fastify";
if ("express" in allDeps) return "express";
if ("@11ty/eleventy" in allDeps) return "eleventy";
if ("node" in (pkg.dependencies ?? {})) return "node";
return "other";
}
async function detectNonJs(projectRoot) {
if (await pathExists(join(projectRoot, "go.mod"))) return "go";
if (await pathExists(join(projectRoot, "Cargo.toml"))) return "rust";
for (const f of [
"requirements.txt",
"pyproject.toml",
"setup.py"
]) if (await pathExists(join(projectRoot, f))) {
try {
const { readText } = await import("./util/fs.mjs");
const content = await readText(join(projectRoot, f));
if (content?.includes("django") || content?.includes("Django")) return "django";
} catch {}
return "other";
}
if (await pathExists(join(projectRoot, "Gemfile"))) {
try {
const { readText } = await import("./util/fs.mjs");
if ((await readText(join(projectRoot, "Gemfile")))?.includes("rails")) return "rails";
} catch {}
return "other";
}
for (const pattern of ["*.csproj", "*.sln"]) {
if (pattern === "*.csproj" && await pathExists(join(projectRoot, "Program.cs"))) return "dotnet";
if (pattern === "*.sln" && await pathExists(join(projectRoot, "Program.cs"))) return "dotnet";
}
if (await pathExists(join(projectRoot, "composer.json"))) {
try {
if (((await readJson(join(projectRoot, "composer.json")))?.require)?.["laravel/framework"]) return "laravel";
} catch {}
return "other";
}
if (await pathExists(join(projectRoot, "pubspec.yaml"))) return "flutter";
if (await pathExists(join(projectRoot, "hugo.toml"))) return "hugo";
if (await pathExists(join(projectRoot, "config.toml"))) try {
const { readText } = await import("./util/fs.mjs");
const content = await readText(join(projectRoot, "config.toml"));
if (content?.includes("baseURL") || content?.includes("hugo")) return "hugo";
} catch {}
if (await pathExists(join(projectRoot, "_config.yml"))) return "jekyll";
return "other";
}
async function detectMonorepo(projectRoot) {
if (await pathExists(join(projectRoot, "pnpm-workspace.yaml"))) return {
isMonorepo: true,
tool: "pnpm workspaces"
};
if (await pathExists(join(projectRoot, "lerna.json"))) return {
isMonorepo: true,
tool: "Lerna"
};
if (await pathExists(join(projectRoot, "nx.json"))) return {
isMonorepo: true,
tool: "Nx"
};
if (await pathExists(join(projectRoot, "turbo.json"))) return {
isMonorepo: true,
tool: "Turborepo"
};
if ((await readJson(join(projectRoot, "package.json")))?.workspaces) return {
isMonorepo: true,
tool: "npm/yarn workspaces"
};
let dir = dirname(projectRoot);
for (let i = 0; i < 5; i++) {
if (await pathExists(join(dir, "pnpm-workspace.yaml"))) return {
isMonorepo: true,
tool: "pnpm workspaces"
};
if (await pathExists(join(dir, "lerna.json"))) return {
isMonorepo: true,
tool: "Lerna"
};
if (await pathExists(join(dir, "nx.json"))) return {
isMonorepo: true,
tool: "Nx"
};
if (await pathExists(join(dir, "turbo.json"))) return {
isMonorepo: true,
tool: "Turborepo"
};
if ((await readJson(join(dir, "package.json")))?.workspaces) return {
isMonorepo: true,
tool: "npm/yarn workspaces"
};
const parent = dirname(dir);
if (parent === dir) break;
dir = parent;
}
return { isMonorepo: false };
}
async function detectFeatures(projectRoot) {
const features = [];
const allDeps = await collectAllDeps(projectRoot);
if ("typescript" in allDeps || await pathExists(join(projectRoot, "tsconfig.json"))) features.push("TypeScript");
for (const lib of [
"vue-i18n",
"@nuxtjs/i18n",
"next-intl",
"react-intl",
"i18next",
"react-i18next"
]) if (lib in allDeps) {
features.push(`i18n (${lib})`);
break;
}
if ("tailwindcss" in allDeps) features.push("Tailwind CSS");
else if ("sass" in allDeps || "scss" in allDeps) features.push("Sass/SCSS");
if ("vitest" in allDeps) features.push("Vitest");
else if ("jest" in allDeps) features.push("Jest");
if ("pinia" in allDeps) features.push("Pinia");
else if ("zustand" in allDeps) features.push("Zustand");
else if ("@reduxjs/toolkit" in allDeps || "redux" in allDeps) features.push("Redux");
return features;
}
async function collectAllDeps(projectRoot) {
const pkg = await readJson(join(projectRoot, "package.json"));
const allDeps = {
...pkg?.dependencies,
...pkg?.devDependencies
};
let dir = dirname(projectRoot);
for (let i = 0; i < 5; i++) {
const parentPkg = await readJson(join(dir, "package.json"));
if (parentPkg?.workspaces) {
Object.assign(allDeps, parentPkg.dependencies, parentPkg.devDependencies);
break;
}
const parent = dirname(dir);
if (parent === dir) break;
dir = parent;
}
return allDeps;
}
//#endregion
export { detectStackInfo as n, inferPlatform as r, detectStack as t };
//# sourceMappingURL=detect-wVSI9VuY.mjs.map
{"version":3,"file":"detect-wVSI9VuY.mjs","names":[],"sources":["../src/util/detect.ts"],"sourcesContent":["import type { StackType, Platform } from '@contentrain/types'\nimport { join, dirname } from 'node:path'\nimport { readJson, pathExists } from './fs.js'\n\ninterface PackageJson {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n workspaces?: string[] | { packages: string[] }\n}\n\nexport interface StackInfo {\n stack: StackType\n platform: Platform\n name: string\n description: string\n monorepo: boolean\n monorepoTool?: string\n features: string[]\n}\n\nconst STACK_META: Record<StackType, { name: string; description: string }> = {\n // Meta-frameworks\n nuxt: { name: 'Nuxt', description: 'Vue meta-framework with SSR/SSG' },\n next: { name: 'Next.js', description: 'React framework with SSR/SSG' },\n astro: { name: 'Astro', description: 'Content-first web framework' },\n sveltekit: { name: 'SvelteKit', description: 'Svelte meta-framework' },\n remix: { name: 'Remix', description: 'Full-stack React framework' },\n analog: { name: 'Analog', description: 'Angular meta-framework' },\n // Plain frameworks\n vue: { name: 'Vue', description: 'Progressive JavaScript framework' },\n react: { name: 'React', description: 'UI library for web and native' },\n svelte: { name: 'Svelte', description: 'Compile-time UI framework' },\n solid: { name: 'Solid', description: 'Reactive UI library' },\n angular: { name: 'Angular', description: 'Platform for web applications' },\n // Mobile\n 'react-native': { name: 'React Native', description: 'Cross-platform mobile framework' },\n expo: { name: 'Expo', description: 'React Native toolchain' },\n flutter: { name: 'Flutter', description: 'Cross-platform UI toolkit (Dart)' },\n // Backend\n node: { name: 'Node.js', description: 'Server-side JavaScript runtime' },\n express: { name: 'Express', description: 'Minimal Node.js web framework' },\n fastify: { name: 'Fastify', description: 'Fast Node.js web framework' },\n nestjs: { name: 'NestJS', description: 'Progressive Node.js framework' },\n django: { name: 'Django', description: 'Python web framework' },\n rails: { name: 'Rails', description: 'Ruby web framework' },\n laravel: { name: 'Laravel', description: 'PHP web framework' },\n go: { name: 'Go', description: 'Go programming language' },\n rust: { name: 'Rust', description: 'Rust programming language' },\n dotnet: { name: '.NET', description: 'Microsoft .NET platform' },\n // Static\n hugo: { name: 'Hugo', description: 'Go-based static site generator' },\n jekyll: { name: 'Jekyll', description: 'Ruby-based static site generator' },\n eleventy: { name: 'Eleventy', description: 'JavaScript static site generator' },\n // Desktop\n electron: { name: 'Electron', description: 'Cross-platform desktop apps' },\n tauri: { name: 'Tauri', description: 'Lightweight desktop apps (Rust)' },\n // Catch-all\n other: { name: 'Other', description: 'Unknown or unsupported framework' },\n}\n\nexport function inferPlatform(stack: StackType): Platform {\n switch (stack) {\n case 'nuxt': case 'next': case 'astro': case 'sveltekit': case 'remix': case 'analog':\n case 'vue': case 'react': case 'svelte': case 'solid': case 'angular':\n return 'web'\n case 'react-native': case 'expo': case 'flutter':\n return 'mobile'\n case 'node': case 'express': case 'fastify': case 'nestjs':\n case 'django': case 'rails': case 'laravel': case 'go': case 'rust': case 'dotnet':\n return 'api'\n case 'electron': case 'tauri':\n return 'desktop'\n case 'hugo': case 'jekyll': case 'eleventy':\n return 'static'\n default:\n return 'other'\n }\n}\n\n/** Backward-compatible: returns just the StackType string */\nexport async function detectStack(projectRoot: string): Promise<StackType> {\n const info = await detectStackInfo(projectRoot)\n return info.stack\n}\n\n/** Rich detection: stack + platform + monorepo + features */\nexport async function detectStackInfo(projectRoot: string): Promise<StackInfo> {\n let stack = await detectFromDeps(projectRoot)\n\n // Walk up for monorepo root if local didn't match\n if (stack === 'other') {\n let dir = dirname(projectRoot)\n for (let i = 0; i < 5; i++) {\n const pkg = await readJson<PackageJson>(join(dir, 'package.json'))\n if (pkg?.workspaces) {\n const rootStack = await detectFromDeps(dir)\n if (rootStack !== 'other') {\n stack = rootStack\n break\n }\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n }\n\n // Non-JS detection if still 'other'\n if (stack === 'other') {\n stack = await detectNonJs(projectRoot)\n }\n\n const mono = await detectMonorepo(projectRoot)\n const features = await detectFeatures(projectRoot)\n const meta = STACK_META[stack]\n\n return {\n stack,\n platform: inferPlatform(stack),\n name: meta.name,\n description: meta.description,\n monorepo: mono.isMonorepo,\n monorepoTool: mono.tool,\n features,\n }\n}\n\nasync function detectFromDeps(dir: string): Promise<StackType> {\n const pkg = await readJson<PackageJson>(join(dir, 'package.json'))\n if (!pkg) return 'other'\n\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n\n // Meta-frameworks (check BEFORE plain frameworks)\n if ('nuxt' in allDeps) return 'nuxt'\n if ('next' in allDeps) return 'next'\n if ('astro' in allDeps) return 'astro'\n if ('@sveltejs/kit' in allDeps) return 'sveltekit'\n if ('@remix-run/node' in allDeps || 'remix' in allDeps) return 'remix'\n if ('@analogjs/platform' in allDeps) return 'analog'\n\n // Mobile (check before plain react)\n if ('expo' in allDeps) return 'expo'\n if ('react-native' in allDeps) return 'react-native'\n\n // Plain frameworks\n if ('vue' in allDeps) return 'vue'\n if ('svelte' in allDeps) return 'svelte'\n if ('solid-js' in allDeps) return 'solid'\n if ('@angular/core' in allDeps) return 'angular'\n if ('react' in allDeps) return 'react'\n\n // Desktop\n if ('electron' in allDeps) return 'electron'\n if ('@tauri-apps/api' in allDeps) return 'tauri'\n\n // Backend (Node.js)\n if ('@nestjs/core' in allDeps) return 'nestjs'\n if ('fastify' in allDeps) return 'fastify'\n if ('express' in allDeps) return 'express'\n\n // Static site generators\n if ('@11ty/eleventy' in allDeps) return 'eleventy'\n\n // Generic Node.js (has package.json but no framework)\n if ('node' in (pkg.dependencies ?? {})) return 'node'\n\n return 'other'\n}\n\nasync function detectNonJs(projectRoot: string): Promise<StackType> {\n // Go\n if (await pathExists(join(projectRoot, 'go.mod'))) return 'go'\n\n // Rust\n if (await pathExists(join(projectRoot, 'Cargo.toml'))) return 'rust'\n\n // Python\n for (const f of ['requirements.txt', 'pyproject.toml', 'setup.py']) {\n if (await pathExists(join(projectRoot, f))) {\n // Check for Django\n try {\n const { readText } = await import('./fs.js')\n const content = await readText(join(projectRoot, f))\n if (content?.includes('django') || content?.includes('Django')) return 'django'\n } catch { /* ignore */ }\n return 'other' // Generic Python — no specific stack type yet\n }\n }\n\n // Ruby\n if (await pathExists(join(projectRoot, 'Gemfile'))) {\n try {\n const { readText } = await import('./fs.js')\n const content = await readText(join(projectRoot, 'Gemfile'))\n if (content?.includes('rails')) return 'rails'\n } catch { /* ignore */ }\n return 'other'\n }\n\n // .NET\n const dotnetFiles = ['*.csproj', '*.sln']\n for (const pattern of dotnetFiles) {\n if (pattern === '*.csproj' && await pathExists(join(projectRoot, 'Program.cs'))) return 'dotnet'\n if (pattern === '*.sln' && await pathExists(join(projectRoot, 'Program.cs'))) return 'dotnet'\n }\n\n // PHP / Laravel\n if (await pathExists(join(projectRoot, 'composer.json'))) {\n try {\n const composer = await readJson<Record<string, unknown>>(join(projectRoot, 'composer.json'))\n const require = composer?.require as Record<string, string> | undefined\n if (require?.['laravel/framework']) return 'laravel'\n } catch { /* ignore */ }\n return 'other'\n }\n\n // Flutter (Dart)\n if (await pathExists(join(projectRoot, 'pubspec.yaml'))) return 'flutter'\n\n // Hugo\n if (await pathExists(join(projectRoot, 'hugo.toml'))) return 'hugo'\n if (await pathExists(join(projectRoot, 'config.toml'))) {\n try {\n const { readText } = await import('./fs.js')\n const content = await readText(join(projectRoot, 'config.toml'))\n if (content?.includes('baseURL') || content?.includes('hugo')) return 'hugo'\n } catch { /* ignore */ }\n }\n\n // Jekyll\n if (await pathExists(join(projectRoot, '_config.yml'))) return 'jekyll'\n\n return 'other'\n}\n\nasync function detectMonorepo(projectRoot: string): Promise<{ isMonorepo: boolean; tool?: string }> {\n if (await pathExists(join(projectRoot, 'pnpm-workspace.yaml'))) return { isMonorepo: true, tool: 'pnpm workspaces' }\n if (await pathExists(join(projectRoot, 'lerna.json'))) return { isMonorepo: true, tool: 'Lerna' }\n if (await pathExists(join(projectRoot, 'nx.json'))) return { isMonorepo: true, tool: 'Nx' }\n if (await pathExists(join(projectRoot, 'turbo.json'))) return { isMonorepo: true, tool: 'Turborepo' }\n\n const pkg = await readJson<PackageJson>(join(projectRoot, 'package.json'))\n if (pkg?.workspaces) return { isMonorepo: true, tool: 'npm/yarn workspaces' }\n\n // Walk up\n let dir = dirname(projectRoot)\n for (let i = 0; i < 5; i++) {\n if (await pathExists(join(dir, 'pnpm-workspace.yaml'))) return { isMonorepo: true, tool: 'pnpm workspaces' }\n if (await pathExists(join(dir, 'lerna.json'))) return { isMonorepo: true, tool: 'Lerna' }\n if (await pathExists(join(dir, 'nx.json'))) return { isMonorepo: true, tool: 'Nx' }\n if (await pathExists(join(dir, 'turbo.json'))) return { isMonorepo: true, tool: 'Turborepo' }\n const parentPkg = await readJson<PackageJson>(join(dir, 'package.json'))\n if (parentPkg?.workspaces) return { isMonorepo: true, tool: 'npm/yarn workspaces' }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n\n return { isMonorepo: false }\n}\n\nasync function detectFeatures(projectRoot: string): Promise<string[]> {\n const features: string[] = []\n const allDeps = await collectAllDeps(projectRoot)\n\n if ('typescript' in allDeps || await pathExists(join(projectRoot, 'tsconfig.json'))) features.push('TypeScript')\n\n const i18nLibs = ['vue-i18n', '@nuxtjs/i18n', 'next-intl', 'react-intl', 'i18next', 'react-i18next']\n for (const lib of i18nLibs) {\n if (lib in allDeps) { features.push(`i18n (${lib})`); break }\n }\n\n if ('tailwindcss' in allDeps) features.push('Tailwind CSS')\n else if ('sass' in allDeps || 'scss' in allDeps) features.push('Sass/SCSS')\n\n if ('vitest' in allDeps) features.push('Vitest')\n else if ('jest' in allDeps) features.push('Jest')\n\n if ('pinia' in allDeps) features.push('Pinia')\n else if ('zustand' in allDeps) features.push('Zustand')\n else if ('@reduxjs/toolkit' in allDeps || 'redux' in allDeps) features.push('Redux')\n\n return features\n}\n\nasync function collectAllDeps(projectRoot: string): Promise<Record<string, string>> {\n const pkg = await readJson<PackageJson>(join(projectRoot, 'package.json'))\n const allDeps: Record<string, string> = { ...pkg?.dependencies, ...pkg?.devDependencies }\n\n let dir = dirname(projectRoot)\n for (let i = 0; i < 5; i++) {\n const parentPkg = await readJson<PackageJson>(join(dir, 'package.json'))\n if (parentPkg?.workspaces) {\n Object.assign(allDeps, parentPkg.dependencies, parentPkg.devDependencies)\n break\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n\n return allDeps\n}\n"],"mappings":"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"}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display