import type { LintDiagnostic } from '../diagnostic.js' import type { SourceFile, SourceImport } from '../source.js' import { isProductionCore, isProductionSource, resolveProjectImport, type DomainProject, } from '../project.js' import { analyzerDiagnostic, type AnalyzerRule } from './project-rule.js' const forbiddenPackages = new Set([ '@astrale-os/adapter-astrale', '@astrale-os/adapter-cloudflare', '@astrale-os/kernel-api', '@astrale-os/kernel-client', '@astrale-os/kernel-server', '@astrale-os/kernel-test', '@astrale-os/sdk', 'node:child_process', 'node:cluster', 'node:dgram', 'node:dns', 'node:fs', 'node:fs/promises', 'node:http', 'node:http2', 'node:https', 'node:net', 'node:tls', 'node:worker_threads', ]) const forbiddenRoots = new Set(['client', 'functions', 'integrations', 'runtime', 'simulation']) const forbiddenRootFiles = new Set(['astrale.config.ts', 'deps.ts', 'domain.ts', 'env.ts']) export const coreIsPure: AnalyzerRule = { slug: 'core-is-pure', analyze(project: DomainProject): readonly LintDiagnostic[] { const diagnostics: LintDiagnostic[] = [] const visited = new Set() const pending = project.files.filter((file) => isProductionCore(file.relativePath)) while (pending.length > 0) { const file = pending.pop()! if (visited.has(file.path)) continue visited.add(file.path) for (const sourceImport of file.imports) { const forbiddenPackage = forbiddenPackageName(sourceImport.specifier) if (forbiddenPackage) { pushDiagnostic( diagnostics, this.slug, file, sourceImport, `Import ${forbiddenPackage} crosses the pure-core boundary.`, ) continue } const dependency = resolveProjectImport(project, file, sourceImport.specifier) if (!dependency || !isProductionSource(dependency.relativePath)) continue if (isForbiddenProjectLayer(dependency.relativePath)) { pushDiagnostic( diagnostics, this.slug, file, sourceImport, `Import ${sourceImport.specifier} reaches ${dependency.relativePath}.`, ) continue } pending.push(dependency) } } return diagnostics }, } function forbiddenPackageName(specifier: string): string | undefined { if (forbiddenPackages.has(specifier)) return specifier for (const name of forbiddenPackages) { if (specifier.startsWith(`${name}/`)) return name } return undefined } function isForbiddenProjectLayer(relativePath: string): boolean { const [root] = relativePath.split('/') return forbiddenRoots.has(root ?? '') || forbiddenRootFiles.has(relativePath) } function pushDiagnostic( diagnostics: LintDiagnostic[], slug: AnalyzerRule['slug'], file: SourceFile, sourceImport: SourceImport, detail: string, ): void { const diagnostic = analyzerDiagnostic(slug, file, sourceImport.token, detail) if (diagnostic) diagnostics.push(diagnostic) }