UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

940 lines (939 loc) 30.5 kB
/** * LanguageProfiles — 全景分析多语言统一注册中心 * * 设计原则: * 1. **语言族 (LanguageFamily)** 是核心抽象 — 同族语言共享 import 语法、框架类体系、生态库 * 2. **单一数据源** — 新增语言只需添加一条 FamilyProfile,所有消费者自动生效 * 3. **与 LanguageService 互补** — LanguageService 管理基础映射 (ext→lang), * LanguageProfiles 管理分析知识 (import 解析、角色推断、技术栈分类) * 4. **按关注点暴露 API** — 消费者调用自己需要的访问器,无需了解内部数据结构 * * 消费者: * - CouplingAnalyzer → importPatterns, sourceExts * - RoleRefiner → familyOf, superclassRoles, protocolRoles, importRolePatterns * - TechStackProfiler → knownLibraries, keywordCategories * - ModuleDiscoverer → skipDirs, artifactSuffixes, vendorDirs, sourceExts * * @module LanguageProfiles */ import { LanguageService } from './LanguageService.js'; /* ═══ Family Definitions ══════════════════════════════════ */ const APPLE = { family: 'apple', languages: ['swift', 'objectivec'], importPatterns: [ // #import <ModuleName/Header.h> { regex: /^#import\s+<([^/]+)\//, extract: (m) => [m[1]] }, // @import ModuleName; { regex: /^@import\s+([A-Za-z_]\w*)(?:\.\w+)*\s*;/, extract: (m) => [m[1]] }, // import ModuleName { regex: /^import\s+([A-Za-z_]\w+)\s*$/, extract: (m) => [m[1]] }, ], superclassRoles: { UIViewController: 'ui', UIView: 'ui', UITableViewCell: 'ui', UICollectionViewCell: 'ui', UINavigationController: 'routing', UITabBarController: 'routing', UIApplication: 'app', NSObject: 'core', NSManagedObject: 'storage', }, protocolRoles: { UITableViewDataSource: 'ui', UITableViewDelegate: 'ui', UICollectionViewDataSource: 'ui', UIApplicationDelegate: 'app', UISceneDelegate: 'app', UIWindowSceneDelegate: 'app', URLSessionDelegate: 'networking', Codable: 'model', Decodable: 'model', Encodable: 'model', }, importRolePatterns: [ { regex: /alamofire|urlsession|afnetworking|moya/i, role: 'networking' }, { regex: /\buikit\b|swiftui|rx.*cocoa|snapkit|masonry/i, role: 'ui' }, { regex: /realm|coredata|fmdb|grdb/i, role: 'storage' }, { regex: /xctest/i, role: 'test' }, ], knownLibraries: { afnetworking: 'Networking', alamofire: 'Networking', moya: 'Networking', urlsession: 'Networking', starscream: 'Networking', socketrocket: 'Networking', sdwebimage: 'Image', kingfisher: 'Image', nuke: 'Image', yyimage: 'Image', flanimatedimage: 'Image', snapkit: 'UI', masonry: 'UI', flexlayout: 'UI', texture: 'UI', asyncdisplaykit: 'UI', iglistkit: 'UI', mbprogresshud: 'UI', svprogresshud: 'UI', yytext: 'UI', dzzfloatingactionbutton: 'UI', herocard: 'UI', swiftui: 'UI', rxswift: 'Reactive', rxcocoa: 'Reactive', reactiveswift: 'Reactive', combine: 'Reactive', openombine: 'Reactive', promisekit: 'Reactive', realm: 'Storage', coredata: 'Storage', fmdb: 'Storage', grdb: 'Storage', wcdb: 'Storage', mmkv: 'Storage', userdefaults: 'Storage', yymodel: 'Serialization', objectmapper: 'Serialization', codable: 'Serialization', swiftyjson: 'Serialization', mantle: 'Serialization', handyjson: 'Serialization', mjextension: 'Serialization', cocoalumberjack: 'Logging', swiftybeaver: 'Logging', oslog: 'Logging', urlnavigator: 'Routing', deeplink: 'Routing', ctmediator: 'Routing', quick: 'Testing', nimble: 'Testing', xctest: 'Testing', ocmock: 'Testing', ohhttpstubs: 'Testing', cryptoswift: 'Security', keychain: 'Security', keychainaccess: 'Security', commoncrypto: 'Security', swinject: 'Architecture', needle: 'Architecture', swiftlint: 'Tooling', r_swift: 'Tooling', swiftgen: 'Tooling', cocoapods: 'Tooling', }, artifactSuffixes: [ '.xcassets', '.bundle', '.lproj', '.framework', '.xcdatamodeld', '.xcodeproj', '.xcworkspace', '.storyboard', '.xib', '.playground', ], vendorDirs: ['Pods', 'Carthage'], extraSkipDirs: ['.build', '.swiftpm', 'DerivedData'], }; const JVM = { family: 'jvm', languages: ['java', 'kotlin'], importPatterns: [ // import com.example.auth.Class → 返回所有包段作为候选 { regex: /^import\s+(?:static\s+)?([\w.]+)/, extract: (m) => { const SKIP = new Set(['java', 'javax', 'android', 'androidx', 'kotlin', 'kotlinx']); return m[1] .split('.') .filter((s) => !SKIP.has(s) && s[0] === s[0].toLowerCase() && s.length > 1); }, }, ], superclassRoles: { Activity: 'ui', AppCompatActivity: 'ui', Fragment: 'ui', DialogFragment: 'ui', View: 'ui', RecyclerViewAdapter: 'ui', Service: 'service', IntentService: 'service', BroadcastReceiver: 'service', ContentProvider: 'storage', ViewModel: 'ui', AndroidViewModel: 'ui', Application: 'app', }, protocolRoles: { Serializable: 'model', Parcelable: 'model', Runnable: 'core', Callable: 'core', OnClickListener: 'ui', Adapter: 'ui', Repository: 'storage', }, importRolePatterns: [ { regex: /retrofit|okhttp|volley/i, role: 'networking' }, { regex: /android\.widget|jetpack.*compose|recyclerview/i, role: 'ui' }, { regex: /room|hibernate|greendao/i, role: 'storage' }, { regex: /junit|espresso|mockito/i, role: 'test' }, ], knownLibraries: { retrofit: 'Networking', okhttp: 'Networking', volley: 'Networking', glide: 'Image', picasso: 'Image', coil: 'Image', compose: 'UI', rxjava: 'Reactive', rxkotlin: 'Reactive', room: 'Storage', hibernate: 'Storage', greendao: 'Storage', gson: 'Serialization', moshi: 'Serialization', jackson: 'Serialization', timber: 'Logging', logback: 'Logging', log4j: 'Logging', arouter: 'Routing', junit: 'Testing', espresso: 'Testing', mockito: 'Testing', hilt: 'Architecture', dagger: 'Architecture', spring: 'Framework', springboot: 'Framework', }, artifactSuffixes: ['.apk', '.aar', '.jar', '.war'], vendorDirs: ['res', 'gen'], extraSkipDirs: ['.gradle', '.idea', 'target'], }; const DART = { family: 'dart', languages: ['dart'], importPatterns: [ // import 'package:module/file.dart' { regex: /^import\s+['"]package:([^/'"]+)/, extract: (m) => [m[1]], }, ], superclassRoles: { StatefulWidget: 'ui', StatelessWidget: 'ui', State: 'ui', ChangeNotifier: 'service', Cubit: 'service', Bloc: 'service', }, protocolRoles: { Widget: 'ui', }, importRolePatterns: [ { regex: /\bdio\b|http_client/i, role: 'networking' }, { regex: /flutter|cupertino|material/i, role: 'ui' }, { regex: /sqflite|hive|objectbox/i, role: 'storage' }, { regex: /flutter_test/i, role: 'test' }, ], knownLibraries: { dio: 'Networking', flutter: 'UI', rxdart: 'Reactive', }, artifactSuffixes: [], vendorDirs: [], extraSkipDirs: ['.dart_tool', '.fvm'], }; const PYTHON = { family: 'python', languages: ['python'], importPatterns: [ // from module import ... / import module { regex: /^(?:from|import)\s+([A-Za-z_]\w*)/, extract: (m) => [m[1]] }, ], superclassRoles: { BaseModel: 'model', Model: 'model', APIView: 'service', ViewSet: 'service', TestCase: 'test', }, protocolRoles: {}, importRolePatterns: [ { regex: /requests|aiohttp|httpx|urllib/i, role: 'networking' }, { regex: /tkinter|pyqt|kivy/i, role: 'ui' }, { regex: /sqlalchemy|django\.db|peewee|tortoise/i, role: 'storage' }, { regex: /pytest|unittest/i, role: 'test' }, ], knownLibraries: { requests: 'Networking', aiohttp: 'Networking', httpx: 'Networking', pillow: 'Image', sqlalchemy: 'Storage', django: 'Framework', flask: 'Framework', fastapi: 'Framework', pytest: 'Testing', asyncio: 'Async', }, artifactSuffixes: [], vendorDirs: [], extraSkipDirs: ['__pycache__', 'venv', '.venv', '.tox'], }; const WEB = { family: 'web', languages: ['javascript', 'typescript'], importPatterns: [ // import ... from 'module' { regex: /^import\s+.*?from\s+['"]([^./'"@][^'"]*?)['"]/, extract: (m) => [m[1].split('/')[0]], }, // import 'module' { regex: /^import\s+['"]([^./'"@][^'"]*?)['"]/, extract: (m) => [m[1].split('/')[0]] }, // require('module') { regex: /require\(\s*['"]([^./'"@][^'"]*?)['"]\s*\)/, extract: (m) => [m[1].split('/')[0]] }, ], superclassRoles: { Component: 'ui', Controller: 'service', Module: 'app', }, protocolRoles: { OnInit: 'ui', OnDestroy: 'ui', CanActivate: 'routing', NestMiddleware: 'service', }, importRolePatterns: [ { regex: /axios|fetch|got|superagent/i, role: 'networking' }, { regex: /react|angular|vue|svelte|next|nuxt/i, role: 'ui' }, { regex: /typeorm|prisma|sequelize|mongoose|knex/i, role: 'storage' }, { regex: /jest|mocha|vitest|cypress|playwright/i, role: 'test' }, { regex: /express|fastify|nestjs|koa/i, role: 'routing' }, ], knownLibraries: { axios: 'Networking', got: 'Networking', superagent: 'Networking', sharp: 'Image', react: 'UI', angular: 'UI', vue: 'UI', svelte: 'UI', tailwindcss: 'UI', bootstrap: 'UI', rxjs: 'Reactive', typeorm: 'Storage', prisma: 'Storage', sequelize: 'Storage', mongoose: 'Storage', knex: 'Storage', express: 'Framework', fastify: 'Framework', nestjs: 'Framework', koa: 'Framework', nextjs: 'Framework', nuxt: 'Framework', jest: 'Testing', mocha: 'Testing', vitest: 'Testing', cypress: 'Testing', playwright: 'Testing', jsonwebtoken: 'Security', passport: 'Security', bcrypt: 'Security', inversify: 'Architecture', tsyringe: 'Architecture', eslint: 'Tooling', prettier: 'Tooling', webpack: 'Tooling', vite: 'Tooling', winston: 'Logging', pino: 'Logging', }, artifactSuffixes: [], vendorDirs: [], extraSkipDirs: ['node_modules', '.next', '.nuxt', 'dist', 'out', 'coverage'], }; const GO = { family: 'go', languages: ['go'], importPatterns: [ // import "path/module" or import alias "path/module" { regex: /^\s*(?:import\s+)?(?:\w+\s+)?"([^"]+)"/, extract: (m) => { const parts = m[1].split('/'); return parts.length > 1 ? [parts[parts.length - 1], parts[parts.length - 2]] : [parts[0]]; }, }, ], superclassRoles: {}, protocolRoles: { Handler: 'service', ReadWriter: 'core', Reader: 'core', Writer: 'core', Stringer: 'utility', }, importRolePatterns: [ { regex: /net\/http|resty/i, role: 'networking' }, { regex: /gin|echo|fiber|mux|chi/i, role: 'routing' }, { regex: /gorm|sqlx|ent/i, role: 'storage' }, { regex: /testing/i, role: 'test' }, ], knownLibraries: { gin: 'Framework', echo: 'Framework', fiber: 'Framework', gorm: 'Storage', }, artifactSuffixes: [], vendorDirs: ['vendor'], extraSkipDirs: [], }; const RUST = { family: 'rust', languages: ['rust'], importPatterns: [ // use crate_name::sub { regex: /^use\s+([a-z_]\w*)::/, extract: (m) => [m[1]] }, // extern crate name { regex: /^extern\s+crate\s+([a-z_]\w*)/, extract: (m) => [m[1]] }, ], superclassRoles: {}, protocolRoles: { Display: 'utility', Debug: 'utility', Serialize: 'model', Deserialize: 'model', Future: 'core', Stream: 'core', Service: 'service', }, importRolePatterns: [ { regex: /reqwest|hyper|surf/i, role: 'networking' }, { regex: /actix|axum|warp|rocket/i, role: 'routing' }, { regex: /diesel|sqlx|sea-orm/i, role: 'storage' }, { regex: /tokio-test/i, role: 'test' }, ], knownLibraries: { reqwest: 'Networking', hyper: 'Networking', actix: 'Framework', axum: 'Framework', rocket: 'Framework', diesel: 'Storage', sqlx: 'Storage', serde: 'Serialization', tokio: 'Async', tracing: 'Logging', }, artifactSuffixes: [], vendorDirs: [], extraSkipDirs: ['target'], }; const DOTNET = { family: 'dotnet', languages: ['csharp'], importPatterns: [ // using Namespace.Sub { regex: /^using\s+(?:static\s+)?([A-Z]\w*)\./, extract: (m) => [m[1]] }, ], superclassRoles: { Controller: 'service', ControllerBase: 'service', DbContext: 'storage', Page: 'ui', }, protocolRoles: { IDisposable: 'core', IEnumerable: 'core', IHostedService: 'service', }, importRolePatterns: [ { regex: /HttpClient|RestSharp/i, role: 'networking' }, { regex: /EntityFramework|Dapper/i, role: 'storage' }, { regex: /xUnit|NUnit|MSTest/i, role: 'test' }, ], knownLibraries: {}, artifactSuffixes: ['.dll', '.exe', '.nupkg'], vendorDirs: [], extraSkipDirs: ['bin', 'obj'], }; /* ═══ Cross-cutting (language-neutral) ════════════════════ */ /** C / C++ 共用 import 模式 — 不属于任何特定生态族 */ const C_CPP_IMPORT_PATTERNS = [ { regex: /^#include\s+<([^/]+)\//, extract: (m) => [m[1]] }, ]; /** 通用 import → 角色推断(任何语言适用) */ const UNIVERSAL_ROLE_PATTERNS = [ { regex: /network/i, role: 'networking' }, { regex: /sqlite/i, role: 'storage' }, { regex: /router|routing|navigation/i, role: 'routing' }, ]; /** 跨平台知名库(不属于单一生态) */ const CROSS_PLATFORM_LIBRARIES = { grpc: 'Networking', protobuf: 'Serialization', sqlite: 'Storage', redis: 'Storage', lottie: 'UI', yoga: 'UI', sentry: 'Diagnostics', firebase: 'Diagnostics', crashlytics: 'Diagnostics', bugly: 'Diagnostics', }; /** 关键词 → 分类的启发式映射 (KNOWN_LIBRARIES 未命中时的 fallback) */ const KEYWORD_CATEGORIES = [ [/net(work)?|http|api|url|request|socket|grpc/i, 'Networking'], [/image|photo|picture|avatar|thumbnail/i, 'Image'], [/ui|view|layout|widget|button|label|cell|collection|table/i, 'UI'], [/anim(at)?|lottie|transition|motion/i, 'Animation'], [/rx|reactive|combine|signal|observable|promise/i, 'Reactive'], [/db|database|sql|realm|store|cache|storage|persist/i, 'Storage'], [/json|model|mapper|serial|codable|parse|decode/i, 'Serialization'], [/log|debug|trace|monitor|crash|sentry|bugly|diagnostic/i, 'Diagnostics'], [/route|router|navigation|deeplink|scheme|mediator/i, 'Routing'], [/test|mock|stub|spec|expect|assert/i, 'Testing'], [/crypto|encrypt|security|keychain|auth|token|oauth/i, 'Security'], [/player|video|audio|media|av|stream/i, 'Media'], [/map|location|geo|coordinate|clocation/i, 'Location'], [/pay|purchase|billing|iap/i, 'Payment'], [/push|notification|apns|message/i, 'Messaging'], [/analytics|track|event|statistics/i, 'Analytics'], [/ad|banner|interstitial|reward/i, 'Advertising'], ]; /** 第三方 / vendor 目录 (跨平台通用) */ const COMMON_VENDOR_DIRS = [ '3rd', 'third_party', 'thirdparty', 'vendor', 'vendors', 'external', 'libs', 'assets', 'resources', 'migrations', 'fixtures', ]; /* ═══ Registry ════════════════════════════════════════════ */ const ALL_FAMILIES = [APPLE, JVM, DART, PYTHON, WEB, GO, RUST, DOTNET]; /** langId → family 查找表 (运行时构建) */ const LANG_TO_FAMILY_MAP = new Map(); for (const fp of ALL_FAMILIES) { for (const lang of fp.languages) { LANG_TO_FAMILY_MAP.set(lang, fp.family); } } // 常见别名也注册(normalize 后可能仍用到原始值) const ALIASES = { 'objective-c': 'objectivec', objc: 'objectivec', scala: 'java', groovy: 'java', clojure: 'java', jsx: 'javascript', tsx: 'typescript', 'c#': 'csharp', golang: 'go', }; for (const [alias, canonical] of Object.entries(ALIASES)) { const fam = LANG_TO_FAMILY_MAP.get(canonical); if (fam) { LANG_TO_FAMILY_MAP.set(alias, fam); } } /** family → profile 快速查找 */ const FAMILY_MAP = new Map(); for (const fp of ALL_FAMILIES) { FAMILY_MAP.set(fp.family, fp); } /* ═══ Merged caches (lazy) ════════════════════════════════ */ let _importPatterns = null; let _knownLibraries = null; let _skipDirs = null; let _artifactSuffixes = null; let _vendorDirs = null; let _thirdPartyPathRegex = null; let _baseClassExclusions = null; let _validCodeLanguages = null; /** * 各语言族中需额外排除的基础类型 — superclassRoles / protocolRoles 未覆盖到的通用根类型。 * getHotNodes() 等统计查询中应剔除这些高入度但无信息量的节点。 */ const EXTRA_BASE_TYPE_EXCLUSIONS = { apple: [ 'UIControl', 'UITableViewController', 'UICollectionViewController', 'UINavigationController', 'UITabBarController', 'NSOperation', 'Any', 'AnyObject', 'Sendable', 'NSCoding', 'NSCopying', ], jvm: [ 'Object', 'ViewGroup', 'RecyclerView.ViewHolder', 'BaseAdapter', 'ArrayAdapter', 'MutableLiveData', 'Cloneable', 'Runnable', ], dart: ['Widget', 'InheritedWidget', 'RenderObject'], python: [ 'object', 'type', 'Exception', 'BaseException', 'ABC', 'Protocol', 'dict', 'list', 'tuple', 'str', 'int', 'float', ], web: [ 'EventTarget', 'HTMLElement', 'Error', 'Promise', 'Map', 'Set', 'Array', 'Function', 'EventEmitter', 'ReadableStream', 'WritableStream', ], go: ['error', 'Stringer', 'Reader', 'Writer', 'Closer', 'Handler'], rust: [ 'Display', 'Debug', 'Clone', 'Copy', 'Send', 'Sync', 'Default', 'Iterator', 'IntoIterator', 'From', 'Into', ], dotnet: [ 'System.Object', 'ValueType', 'Enum', 'Exception', 'IEnumerable', 'IComparable', 'Task', 'MonoBehaviour', ], universal: [ 'Object', 'Any', 'Unit', 'Nothing', 'Companion', 'Component', 'PureComponent', 'React.Component', ], }; /* ═══ LanguageProfiles — Static API ═══════════════════════ */ export class LanguageProfiles { /* ─── Family Resolution ─────────────────────── */ /** 将规范化语言 ID 映射到语言族 */ static familyOf(langId) { const normalized = LanguageService.normalize(langId); return LANG_TO_FAMILY_MAP.get(normalized) ?? LANG_TO_FAMILY_MAP.get(langId.toLowerCase()); } /** 返回所有已注册的语言族 ID */ static allFamilies() { return ALL_FAMILIES.map((fp) => fp.family); } /** 根据主语言解析项目涉及的语言族 */ static resolveFamilies(primaryLang) { if (!primaryLang) { return ALL_FAMILIES.map((fp) => fp.family); } const fam = LanguageProfiles.familyOf(primaryLang); return fam ? [fam] : ALL_FAMILIES.map((fp) => fp.family); } /* ─── CouplingAnalyzer: Import Extraction ───── */ /** * 获取所有 import 解析模式 (合并全部语言族 + C/C++) * * CouplingAnalyzer 对每行代码尝试所有模式, * 按「特异性递减」排列:最特殊的模式在前。 */ static get importPatterns() { if (!_importPatterns) { _importPatterns = []; for (const fp of ALL_FAMILIES) { _importPatterns.push(...fp.importPatterns); } _importPatterns.push(...C_CPP_IMPORT_PATTERNS); } return _importPatterns; } /** * 源代码文件扩展名集合 — 委托 LanguageService * * 消除 CouplingAnalyzer / ModuleDiscoverer 自建 SOURCE_EXTS 的重复。 */ static get sourceExts() { return LanguageService.sourceExts; } /* ─── RoleRefiner: Role Inference ───────────── */ /** * 合并指定语言族的超类→角色映射 * @param families 项目检测到的语言族 */ static superclassRoles(families) { const merged = {}; for (const fam of families) { const fp = FAMILY_MAP.get(fam); if (fp) { Object.assign(merged, fp.superclassRoles); } } return merged; } /** * 合并指定语言族的协议/接口→角色映射 * @param families 项目检测到的语言族 */ static protocolRoles(families) { const merged = {}; for (const fam of families) { const fp = FAMILY_MAP.get(fam); if (fp) { Object.assign(merged, fp.protocolRoles); } } return merged; } /** * 合并指定语言族的 import→角色模式 + 通用模式 * @param families 项目检测到的语言族 */ static importRolePatterns(families) { const patterns = []; for (const fam of families) { const fp = FAMILY_MAP.get(fam); if (fp) { patterns.push(...fp.importRolePatterns); } } patterns.push(...UNIVERSAL_ROLE_PATTERNS); return patterns; } /* ─── TechStackProfiler: Library Classification ── */ /** * 获取全量已知库→分类映射 (合并所有族 + 跨平台库) * * TechStackProfiler 不按族过滤 — 外部依赖可能跨生态 */ static get knownLibraries() { if (!_knownLibraries) { _knownLibraries = {}; for (const fp of ALL_FAMILIES) { Object.assign(_knownLibraries, fp.knownLibraries); } Object.assign(_knownLibraries, CROSS_PLATFORM_LIBRARIES); } return _knownLibraries; } /** 关键词启发式分类 — KNOWN_LIBRARIES 未命中时的 fallback */ static get keywordCategories() { return KEYWORD_CATEGORIES; } /* ─── ModuleDiscoverer: Filesystem Heuristics ── */ /** * 应跳过的目录名集合 (合并 LanguageService.scanSkipDirs + 各族额外目录) */ static get skipDirs() { if (!_skipDirs) { _skipDirs = new Set(LanguageService.scanSkipDirs); for (const fp of ALL_FAMILIES) { for (const d of fp.extraSkipDirs) { _skipDirs.add(d); } } // panorama 特有的跳过目录 _skipDirs.add('.autosnippet'); } return _skipDirs; } /** 构建产物后缀 (合并全部族) */ static get artifactSuffixes() { if (!_artifactSuffixes) { const set = new Set(); for (const fp of ALL_FAMILIES) { for (const s of fp.artifactSuffixes) { set.add(s); } } _artifactSuffixes = [...set]; } return _artifactSuffixes; } /** Vendor / 第三方目录名集合 (合并通用 + 各族) */ static get vendorDirs() { if (!_vendorDirs) { _vendorDirs = new Set(COMMON_VENDOR_DIRS); for (const fp of ALL_FAMILIES) { for (const d of fp.vendorDirs) { _vendorDirs.add(d); } } } return _vendorDirs; } /* ─── Cross-cutting: Third-party Path Detection ──── */ /** * 三方库路径正则 — 匹配路径中的 vendor 目录名或已知库名 * * 组成: * 1. vendorDirs + 常见 skip 目录 (Pods, Carthage, DerivedData, …) * 2. knownLibraries 中所有库名 (首字母大写形式) * * 用于 Agent 工具层对搜索结果做三方库过滤。 */ static get thirdPartyPathRegex() { if (!_thirdPartyPathRegex) { // 1. 目录名部分 const dirNames = new Set([ ...LanguageProfiles.vendorDirs, // 额外常见三方/构建产物目录 'Pods', 'Carthage', '.build/checkouts', 'DerivedData', 'Submodules', 'ThirdParty', 'include', 'node_modules', 'build', ]); const dirPart = [...dirNames].map((d) => d.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'); // 2. 知名库名部分 — 从 knownLibraries 提取,还原首字母大写 const libKeys = Object.keys(LanguageProfiles.knownLibraries); // 取原始 casing:capitalize 首字母 const libNames = new Set(); for (const key of libKeys) { if (key.length >= 3) { libNames.add(key.charAt(0).toUpperCase() + key.slice(1)); } } const libPart = [...libNames].map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'); _thirdPartyPathRegex = new RegExp(`(?:^|/)(?:${dirPart})/|(?:^/)(?:${libPart})/`, 'i'); } return _thirdPartyPathRegex; } /* ─── HotNodes: Base Class Exclusions ────────── */ /** * 多语言基类/根类型排除集 — 合并所有族的 superclassRoles + protocolRoles + 额外基础类型。 * * 用于 getHotNodes() 等入度统计,排除高入度但无信息量的语言根类型。 * 新增语言族时自动生效,无需手动维护排除列表。 */ static get baseClassExclusions() { if (!_baseClassExclusions) { const set = new Set(); // 从各族的 superclassRoles / protocolRoles keys 自动聚合 for (const fp of ALL_FAMILIES) { for (const name of Object.keys(fp.superclassRoles)) { set.add(name); } for (const name of Object.keys(fp.protocolRoles)) { set.add(name); } } // 追加额外基础类型 for (const names of Object.values(EXTRA_BASE_TYPE_EXCLUSIONS)) { for (const name of names) { set.add(name); } } _baseClassExclusions = set; } return _baseClassExclusions; } /* ─── QualityScorer: Valid Code Languages ─────── */ /** * 合法代码语言集合 — 合并 LanguageService.knownLangs + 常见别名。 * * QualityScorer 格式评分使用,判断 recipe 的 language 字段是否合法。 * 新增语言时只需在 LanguageService 添加,此处自动生效。 */ static get validCodeLanguages() { if (!_validCodeLanguages) { const set = new Set(LanguageService.knownLangs); // 添加常见别名,使 QualityScorer 能宽容匹配 const extraAliases = [ 'objective-c', 'objc', 'c#', 'golang', 'shell', 'bash', 'zsh', 'markdown', 'md', 'json', 'yaml', 'yml', 'toml', 'sql', 'graphql', 'html', 'css', 'scss', 'less', 'jsx', 'tsx', 'scala', 'groovy', 'clojure', 'lua', 'perl', 'r', 'matlab', 'haskell', 'elixir', 'erlang', 'zig', 'nim', 'v', ]; for (const alias of extraAliases) { set.add(alias); } _validCodeLanguages = set; } return _validCodeLanguages; } }