import fs from "node:fs"; import path from "node:path"; import { readManifestSource } from "./_manifest-source.js"; import { readWorkspacePackages } from "./_workspace.js"; /** * Check whether `packages/core-` exists under any of the workspace * globs. The glob set is small and predictable (e.g. ["apps/*", "packages/*"]); * we simulate matching by checking each glob's directory portion + verifying * `core-` exists in that directory. */ function coreExistsInWorkspace(coreName, repoRoot, packageGlobs) { for (const glob of packageGlobs) { const slashStar = glob.endsWith("/*") ? glob.slice(0, -2) : null; if (!slashStar) continue; const candidate = path.join(repoRoot, slashStar, `core-${coreName}`); if (fs.existsSync(candidate)) return true; } return false; } /** @type {import("eslint").Rule.RuleModule} */ export default { meta: { type: "problem", docs: { description: "Cores declared in a feature.manifest.ts's requiredCores must exist as core- packages within a workspace glob.", }, schema: [ { type: "object", properties: { repoRoot: { type: "string" }, }, additionalProperties: false, }, ], messages: { coreNotInstalled: "Manifest declares requiredCores: [..., \"{{core}}\", ...] but `core-{{core}}` is not present in any workspace glob. Run `pnpm turbo gen core-package {{core}}` or drop the entry.", }, }, create(context) { const opts = context.options[0] ?? {}; const repoRoot = opts.repoRoot ?? context.cwd ?? process.cwd(); return { Program(node) { const filename = context.filename; if (!filename.endsWith("/feature.manifest.ts") && !filename.endsWith("\\feature.manifest.ts")) { return; } const manifest = readManifestSource(filename); if (!manifest) return; const globs = readWorkspacePackages(repoRoot); for (const core of manifest.requiredCores) { if (!coreExistsInWorkspace(core, repoRoot, globs)) { context.report({ node, messageId: "coreNotInstalled", data: { core }, }); } } }, }; }, };