設定
導入完成後,blueprint.config.mjs 就是專案的架構權威。你可以匯出經 defineBlueprint() 驗證的物件,或使用內建預設設定:
import { defineBlueprint } from '@kekkai/blueprint';
export default defineBlueprint({
name: 'storefront',
framework: 'react',
architecture: {
alias: '~app',
layers: [
{ name: 'pages', does: '組合路由頁面。' },
{ name: 'components', does: '介面呈現,包含功能私有元件。' },
{ name: 'services', does: '網路存取。', owns: ['axios'] },
],
},
});defineBlueprint() 會立刻驗證;Blueprint 載入一般物件時也會再次驗證。未知的結構欄位與 不成立的相依圖會回報明確錯誤,不會默默忽略。API 參考保留完整型別簽章; 本頁則說明這些欄位如何組成同一個模型。
套件提供兩個具有完整宣告檔的公開入口。架構編寫、預設設定、emitters 與 runtimes 使用 @kekkai/blueprint;需要組合 Blueprint 管理的執行期或產生文字時,整合工具可從 @kekkai/blueprint/operational-contract 使用具型別的 OperationalText renderers。一般導入 不需要使用此子路徑;確有整合需求時應匯入這個公開入口,不要直接引用 dist/ 內部檔案。
根欄位
name- 型別與預設值:選填
string。 - 用途:顯示於架構手冊與 Agent 守則的專案名稱。
- 型別與預設值:選填
framework- 型別與預設值:必填
'vue' | 'react' | 'auto'。 - 用途:決定原始碼 glob 與框架限定規則;
auto交由執行期偵測。
- 型別與預設值:必填
architecture- 型別與預設值:必填
ArchitectureDef。 - 用途:定義原始碼根目錄、拓樸、依賴方向、單元配置、別名與所有權。
- 型別與預設值:必填
rules- 型別與預設值:
Record<string, RuleSetting>,預設{}。 - 用途:選用的 lint/執行期關卡與文件判斷準則。規則有備用數值,不代表未宣告時會自動啟用。
- 型別與預設值:
principles- 型別與預設值:
PrincipleDef[],預設[]。 - 用途:產生給人與 Agent 閱讀的核心工程信念。
- 型別與預設值:
componentShape- 型別與預設值:
AxisDef[],預設[]。 - 用途:各自獨立的元件設計判斷軸線。
- 型別與預設值:
playbook- 型別與預設值:
PlaybookSection[],預設[]。 - 用途:依主題分組的工作規則。
- 型別與預設值:
emit- 型別與預設值:選填
EmitDef。 - 用途:架構手冊、Agent 守則與結構性 lint 的產出政策。
- 型別與預設值:選填
架構
別名與原始碼根目錄
architecture: {
alias: '~app',
additionalAliases: {
'@legacy': './src',
'@shared': './src/shared',
},
sourceRoot: 'src',
// ...
}alias必填且沒有預設值,是跨越受管理模組或分層邊界時唯一標準的原始碼根別名。additionalAliases將既有別名對應至專案相對路徑,供解析與相依診斷使用;它們不會 變成跨邊界時的另一套標準寫法。 若在此重複標準alias,只有正規化後仍指向相同原始碼根目錄才會接受;指向不同位置時, 會在設定驗證階段直接拒絕,不讓互相矛盾的別名身分進入 lint 產出、檢查或遷移建議。sourceRoot預設為src。Next.js 未使用src/等根目錄配置可設為.。Lint、inspect、deps、產生後的守則與建置路徑都以它為基準。
Layer-first
沒有 architecture.modules 時,實體結構是 Layer → Unit:
architecture: {
alias: '~app',
layers: [
{ name: 'pages', does: '路由與頁面組合。' },
{ name: 'components', does: '介面呈現,包含功能私有元件。' },
{ name: 'services', does: '網路存取原語。' },
],
}分層宣告順序就是預設的單向流:前面的層可以匯入後面的層,反向則不行。以上例來說, pages 可匯入 components 與 services,services 則不能匯入上游。
Module-first
architecture.modules 以「有沒有宣告」決定拓樸:
- 省略 — layer-first。
[]— module-first 起點:拓樸已宣告,但還沒有任何領域模組。
所有內部分層、規則與理念都已生效;lint、inspect、deps、doctor、impact、
架構手冊與 Agent 守則都會把這個應用程式當成 module-first。
空的起點是刻意的狀態,不是導入沒做完。- 一個以上的模組 — 已建立領域模組的 module-first。
宣告模組後,一般模組的實體結構改為 Module → Layer → Unit:
architecture: {
alias: '~app',
modules: [
{ name: 'checkout', does: '結帳流程。', dependsOn: ['catalog'] },
{ name: 'catalog', does: '商品探索。' },
{ name: 'app', does: '路由組合。' },
],
layers: [
{ name: 'components', does: '模組介面。' },
{ name: 'hooks', does: '狀態轉接。' },
{ name: 'services', does: '資料存取。' },
],
layerFiles: '{module}/{layer}/**/*.{ts,tsx}',
}每個一般模組都重複使用同一份分層定義,模組根目錄的檔案則佔 container 的位置:
它們負責組合自己與可達模組的內部分層,所以 layer-first 的 containers 層不會在模組裡重複出現。dependsOn 列出直接依賴;實際權限依相依圖的 遞移可達性決定,與宣告順序無關。一次受管理的匯入必須同時通過外層模組圖與內部分層流。
模組名稱即使只有大小寫不同也不能重複。相依目標必須已宣告,且不得為空、重複、指向 自己或形成循環。
app 是選用的保留名稱。它代表 container 位置上的遞迴路由組合,不是一般領域模組, 也不重複內部分層。
原始碼中的直接模組相依
Module-first 專案的 deps 與 inspect 會分別呈現三種相依圖:
| 相依圖 | 意義 |
|---|---|
D:宣告的直接相依 | modules[].dependsOn 寫下的每條相依邊;即使另有間接路徑,也保留直接宣告。 |
P:匯入權限 | 從 D 推導出的所有可達模組;模組匯入權限仍由它決定。 |
O:觀察到的直接相依 | 依目前受管原始碼與標準模組身分,確認的跨模組直接參照。 |
例如宣告 a -> b -> c 時,a 匯入 c 仍有間接權限,但精確同步比對會指出 a.dependsOn 缺少 c。若 a 沒有直接參照 b,只有在 a 的分析完整時,才能將該宣告 列為未使用。觀察到的相依不會授予權限;流向、別名、相對路徑跨界與深入匯入違規仍獨立回報。
觀察範圍包含靜態匯入、副作用匯入、型別匯入與再匯出,以及可靜態確認目標的 require() 和動態匯入。來源須位於 sourceRoot,使用既有掃描器支援的 JS/TS/JSX/TSX/模組/ CommonJS/Vue 格式;套件相依、建置輸出、涵蓋率與快取目錄仍依既有規則排除。 符合 testFiles 的檔案不納入相依或不確定性;testFiles: [] 則取消豁免。 layerFilesIgnore 只排除 lint 與以 lint 為依據的檢查,不會隱藏模組相依。 產生的原始碼只要仍在受管範圍內,也照常計算;檔名、檔頭或註解都不是豁免依據。 同模組參照、沒有模組身分的來源根目錄組裝檔、外部套件及無法分類的目標不會形成跨模組相依。
執行時才能決定的匯入、解析失敗,以及無法解析的專案內參照,只會讓匯入者所屬模組的分析 不完整。已確認的相依仍保留;該模組未觀察到的宣告標為 unverified,不能當成 unused。 已可靠解析到來源目錄外的目標不算未知模組。外部使用端沒有模組身分;再廣的 consumerFiles 模式也不會改變受管檔案的身分。模組分析與外部範圍證據的完整性分開判定,因此模組結果可以是 synchronized,而外部證據仍不完整或無法取得。這是上述範圍內的原始碼觀察結果,並非完整的 執行時相依圖。
| 狀態 | 意義 |
|---|---|
synchronized | D = O,且模組分析完整。 |
drift | 已確認缺少或未使用的宣告;即使同時有不確定性,也維持此狀態。 |
indeterminate | 尚無已確認的差異,但不確定性使精確比對無法完成。 |
runway | Module-first 宣告了 modules: []。 |
每個模組也有 reach:至少分析一個受管檔案時為 covered,否則為 vacuous。 空原始碼樹上的集合相等,不代表架構已有實際涵蓋範圍,也不證明領域所有權合理。 Layer-first 的 moduleDependencies 為 null,文字報告不顯示模組區段。
要啟用精確同步,先檢視 blueprint deps 或 blueprint inspect,判斷應修正原始碼還是宣告、 處理不確定性,再明確設定所需層級:
rules: {
moduleDependencySync: 'error',
}moduleDependencySync 接受 error、warn 或 off,沒有數值或自訂選項。 它由 Inspect 執行,不是 ESLint 規則,也不受 emit.lint.severity 影響。 未設定或設為 off 時仍可查看報告,但不產生同步檢查結果;warn 產生警告,error 則依 Inspect 原有規則影響結束狀態。Layer-first 會標示此關卡不可用;空模組起步狀態不產生差異問題。 既有專案與內建預設不會自動啟用,不需要生命週期遷移或改寫設定。Blueprint 不會自動新增、 移除、排序或依間接路徑刪減 dependsOn。觀察到禁止的相依,也不代表應直接補上權限。
一般模組的根單元
一般模組的根目錄只要有直接受管理的原始碼檔案,就形成一個以模組目錄為位址的結構單元。 它必須有且只有一個支援的 index.* 入口;其他直接成員都是私有檔案。外部呼叫端使用模組 別名本身(例如 ~app/checkout),不得加上 /index 或私有檔名。私有成員必須能從入口 透過匯入或重新匯出的鏈結到達。Inspect 會以 orphan-root-file 回報已證明無法到達的成員, 也包含與入口斷開的循環;解析失敗與執行期才能決定的匯入仍列為分析限制。 入口缺失或不唯一時,應先處理入口問題,不能據此斷言其他成員都是孤立檔案。
已宣告內部分層的檔案仍屬於各自的單元。只有內部分層的模組不需要根入口或空的彙整檔, 任意子目錄也不會因此合法化。同一根單元內可使用相對匯入;跨界仍須遵守標準別名與分層 入口規則。內部分層不能向上匯入模組根單元。保留的遞迴 app 樹不套用根入口、私有路徑 或孤立成員檢查。相依圖維持模組/container 身分,不會把私有檔案變成公開架構單元。
舊專案跨越 4.2 升級時,必須人工完成 review-module-root-unit。已符合契約的根單元可保留 原位;Blueprint 不會代選匯出或產生應用程式 API。layer-first 專案只需確認不必遷移模組根單元。 只有未修改的標準 React/Vue 預設架構,才建議將呈現責任放在 components。所有自訂架構 都必須由負責人決定位置,即使分層名為 components,或只有 views.does 看似明確描述 呈現責任,也不能自動選定。執行工具不會解讀責任文字、增加分層或修改既有責任。 檔案私有或只有一個使用者,都不足以證明它負責流程協調。
LF → MF 時,應在保留的轉換來源與逐檔決策中記錄原路徑、單元身分、分層定義與自訂入口名。 多個 LF 單元合併成一個 MF 根單元,也不會抹除原身分。MF → LF 時,有效來源紀錄應逐檔還原 至原單元,保留 Screen 等原入口名稱;MF 的固定 index 規則不能覆寫原 LF 入口。 只有完全沒有保留 LF 來源時,才可提議 containers/<module> 作為備選,並檢查負責人決策、 路徑衝突與目標入口。來源不完整、矛盾或無法對應時,必須明列待決事項,不得改用備選、 遺漏成員或重建已不存在的單元。MF 新增而無來源的成員也須明確決定歸屬。 驗證必須涵蓋目的地唯一性與完整性、大小寫衝突、匯出、改寫後的參照,以及 LF 的入口與相依規則。 歷史身分還原與呈現責任歸屬是兩項不同的審查;衝突時交由負責人決定,不得靜默覆寫來源紀錄。 只驗證路徑的工具無法證明任意 API 改動仍等價。
分層欄位
name- 型別與預設值:必填非空
string。 - 意義:資料夾/分層識別碼。不得重複,也不能是路徑或保留的產出檔名。
- 型別與預設值:必填非空
does- 型別與預設值:必填
string,不可為空字串或只含空白。 - 意義:一行責任說明,會寫入產出守則。
- 型別與預設值:必填
mustNot- 型別與預設值:
(string | ExecutableMustNot)[],預設[]。 - 意義:依宣告順序保留人工審查責任與選擇啟用的可執行限制。見可執行的 mustNot。
- 型別與預設值:
layout- 型別與預設值:
'folder' | 'file',預設'file'。 - 意義:Folder layout 的單元具有公開入口;file layout 採分層粒度的相依關係。
- 型別與預設值:
entry- 型別與預設值:
string,預設'index'。 - 意義:Folder layout 單元的公開入口檔名。
- 型別與預設值:
allowedImporters- 型別與預設值:選填
(string | AllowedImporter)[]。 - 意義:縮小哪些上游層可以匯入本層;省略時允許所有較早宣告的層。
- 型別與預設值:選填
owns- 型別與預設值:
OwnedPrimitive[],預設[]。 - 意義:只允許本層使用的套件、具名匯入或全域物件。
- 型別與預設值:
lintOverrides- 型別與預設值:
Record<string, unknown>,預設{}。 - 意義:本層的 ESLint 覆寫;Blueprint 管理的限制規則不能在這裡取代。
- 型別與預設值:
物件形式的 importer 可加入 selfOnly 與 description:
{
name: 'contexts',
does: '定義 Context 與 Provider。',
allowedImporters: [
{ layer: 'containers', description: '只掛載 Provider' },
{ layer: 'hooks', selfOnly: true, description: '只使用 Context' },
],
}selfOnly 允許依賴,但禁止繼續再匯出。每個 importer 必須是不重複、且已在更前面宣告的 分層,因此這個欄位只能縮小單向圖,不能建立回頭依賴。
所有權有三種寫法:
owns: [
'axios',
{ package: 'react', imports: ['useContext'] },
{ package: '@company/*', pattern: true, exempt: ['**/*.adapter.ts'] },
{ global: 'WebSocket' },
]- 字串代表擁有完整套件/模組識別字。
{ package, imports }只擁有具名匯入;pattern將套件名稱視為 glob 群組,exempt則排除符合條件的檔案。{ global }代表不經 import 宣告的全域物件。
檔案範圍與命名
layerFiles- 預設值:依框架產生原始碼 glob。
- 意義:可填一個或多個 glob。Layer-first 必須包含
{layer};module-first 必須同時包含{module}與{layer}。
layerFilesIgnore- 預設值:無。
- 意義:從產生的 lint 與 lint 型 finding 排除;其他 inspect 分析仍可能看見。
testFiles- 預設值:
**/*.test.{js,jsx,ts,tsx,vue}與**/*.spec.{js,jsx,ts,tsx,vue}。 - 意義:從結構/度量分析與相依圖排除,同時作為測試限定規則範圍。
[]會關閉兩者,也讓testFilename沒有作用範圍。
- 預設值:
naming- 預設值:
{}。 - 意義:依概念命名的文字慣例,會寫入架構手冊與 Agent 守則。
- 預設值:
可攜式 glob 使用 /、**、*、? 與 *.{ts,tsx} 這類單層大括號選項。Lint 與 inspect 共用的語法不包含反向條件、字元集合、extglob 或巢狀大括號。
外部使用端
architecture.consumerFiles?: string | string[] 用來明確宣告哪些原始碼檔案會使用受管應用程式, 但不屬於模組 → 分層 → 單元拓樸。預設不宣告任何外部使用端;省略欄位或填入 [] 都維持既有 行為。單一字串等同只有一筆模式的陣列;非字串、空字串或只有空白的模式都會讓設定驗證失敗。
consumerFiles: ['.storybook/preview.ts', 'e2e/**/*.{ts,tsx}', 'scripts/seed.ts'],模式使用與 testFiles 相同的可攜式比對器,路徑以 Blueprint 應用程式根目錄為基準, 不是 sourceRoot 或程序的暫存目錄。支援 /、**、*、? 與單層大括號選項;不支援 否定、字元類別、延伸 glob 或巢狀大括號。調換模式順序或重複等效模式,都不影響分類結果。
只有已掃描且位於 sourceRoot 外的檔案能取得這個角色。即使 **/*.stories.tsx 同時命中 受管原始碼,也不會豁免根目錄內的檔案。sourceRoot: '.' 沒有根目錄外的原始碼集合,因此 宣告不會產生效果。Layer-first 的行為不變。預設設定、框架與工具名稱都不會自動宣告外部 使用端;模式應盡量精確,過寬的宣告可能掩蓋真正遺漏在架構外的應用程式原始碼。
Module-first 的範圍分析允許已宣告的外部使用端匯入受管原始碼,不因此回報 uncovered-application-source;受管原始碼反向匯入外部使用端,仍然是錯誤。分類先按檔案 判定,再彙整資料夾,因此同一資料夾裡未宣告的匯入者仍會被回報。外部使用端沒有模組、分層、 單元、容器、所有權或公開入口身分,也不會增加 dependsOn、單元相依圖或架構涵蓋範圍的證據。 這只是原始碼範圍的區分,不代表已驗證標準別名、深入匯入、目標 API 或公開入口的使用是否正確。
testFiles 保留獨立政策,並且優先處理:符合的測試檔會先從兩個方向排除,再進行使用端分類。 layerFilesIgnore 不能隱藏範圍證據;掃描排除規則、additionalAliases 與基準線也維持原有用途。 宣告不會搬移或建立檔案、擴大掃描範圍,或壓掉其他適用的問題。產生的手冊與 Agent 指引只記錄 宣告模式及其契約,不嵌入會隨原始碼變動而過期的命中檔案清單。是否啟用由專案負責人決定, 既有設定不需要遷移。
規則
規則可直接填 tier,或使用含 tier 與選項的物件:
rules: {
maxLines: { tier: 'error', value: 400 },
usePrefix: { tier: 'error', layer: 'hooks', prefix: 'use' },
codeStyle: { tier: 'warn', indent: 2, quotes: 'single', semi: true, maxLen: 90 },
deadCode: 'warn',
}有效 tier 為 error、warn、off。未宣告的選用規則不會產生。度量規則只有在已宣告但 省略 value 時,才會採用備用數值。
maxLines— 由max-lines執行;備用值為400,只計算程式行。maxLinesPerFunction— 由max-lines-per-function執行;備用值為100,只計算程式行。maxParams— 由max-params執行;備用值為3。maxStatements— 由max-statements執行;備用值為15。complexity— 由complexity執行;備用值為12。unusedVars— 由 core 或 TypeScript 版no-unused-vars執行;底線開頭的參數可忽略,變數改成底線開頭不算刪除。explicitAny— 由@typescript-eslint/no-explicit-any執行;只有提供 TypeScript 外掛時產生。codeStyle— 由@stylistic規則組與curly執行;預設縮排2、單引號、分號、最大行長90。statementsPerLine— 由@stylistic/max-statements-per-line執行;固定最多1個 statement。statementPadding— 由@stylistic/padding-line-between-statements執行;採固定留白政策。importBlock— 由import-x/first與import-x/no-duplicates執行;需要 import-x 外掛。fixtureImports— 由結構性 restricted imports 執行;禁止正式程式碼從別名下的fixtures路徑匯入。deepWatch— 由blueprint/no-deep-watch執行;只適用 Vue。usePrefix— 由blueprint/use-prefix執行;預設分層為hooks、前綴為use。usePrefixReactivity— 由blueprint/use-prefix-needs-reactivity執行;檢查use命名單元是否在同一檔案直接呼叫可辨識的響應式/生命週期 API。它不會追查被呼叫的其他 hooks,因此警告不能證明函式是純函式;重新命名或搬移前,應先檢查組合的 hooks。testFilename— 由blueprint/test-filename-matches-source執行;使用architecture.testFiles,空陣列時無法啟用。typedefOnlyFile— 由blueprint/no-typedef-only-file執行;只適用 JavaScript 檔案。moduleDependencySync— 由 Inspect 執行的 module-first 選用關卡,精確比對直接相依;詳見原始碼中的直接模組相依。cycles— 由inspect的循環 finding 執行;執行指令或 CI 時分析,預設不是 ESLint 規則。deadCode與其他識別碼 — 只寫入產出守則,沒有機器關卡;可另用 knip 等合適工具。
結構性規則不屬於這份選用清單。只要執行 emitLint,模組可達性、分層流、標準別名、單元 入口、相對路徑逃逸、所有權與 selfOnly 就會由 architecture 產生。
請用 blueprint rules 查看真實專案的有效規則。若自行呼叫 emitLint,第三方規則需要在 EmitLintOptions 提供對應的 typescript、stylistic 或 imports 外掛;自動產生的設定檔 已經完成接線。
核心信念、元件設計與工作指南
這三組欄位承載專案自己的工程理念,不假裝所有判斷都能自動化:
principles: [{
id: 'single-source',
say: '維持單一真相來源。',
why: '衍生值不該成為重複的可變狀態。',
land: 'claude',
}],
componentShape: [{
id: 'narrow-inputs',
name: '收窄輸入',
say: '單元只依賴真正需要的內容。',
why: '介面越小,耦合越少。',
triage: 'max-params',
}],
playbook: [{
title: '審查',
rules: [{ id: 'measure-first', say: '最佳化前先量測。' }],
}],- principle 需要不重複的
id、say、why與land(lint或claude)。 - component axis 需要不重複的
id、name、say與why;triage可指定用來找出候選 問題的規則。 - 每個執行指南區段都要有非空
title;規則 id 必須在所有區段間保持唯一,並 提供say與選填why。
Vue 與 React 預設設定會帶入 Blueprint 的標準理念;自訂設定可保留、替換或省略。概念本身 見工程理念。
產出目標
emit: {
handbook: 'docs/architecture-handbook.md',
agents: [
'agents',
{ target: 'cursor', path: '.cursor/rules/architecture.mdc' },
],
lint: { severity: 'warn' },
}emit.handbook- 預設值:
docs/architecture-handbook.md。 - 意義:相對於專案根目錄的架構手冊路徑。
- 預設值:
emit.agents- 預設值:
['claude', 'agents']。 - 意義:守則目標。可用值為
claude、agents、gemini、copilot、cursor、windsurf;物件形式可改路徑,[]代表完全不產生。
- 預設值:
emit.lint.severity- 預設值:
'error'。 - 意義:只控制結構性規則;選用
rules仍使用各自 tier。
- 預設值:
Agent 目標不能重複,自訂路徑也不能是空字串。預設路徑、合併/完整管理策略與生命週期見 產出檔案。
預設設定
import { nextPreset, reactPreset, vuePreset } from '@kekkai/blueprint';
export default vuePreset({ name: 'admin', alias: '~app' });
// export default reactPreset({ name: 'web', emit: { agents: ['agents'] } });
// export default nextPreset({ router: 'app', srcDir: true });vuePreset() 與 reactPreset() 提供標準單向應用程式分層、所有權、規則、核心信念、元件 軸線與工作指南。選項為 name、alias(預設 ~app)、emit 與 modules。
modules 跟 architecture.modules 用同一條「有沒有宣告」規則。
省略時,預設設定維持 layer-first 分層。
給 [] 或宣告模組時,預設設定會投影成 module-first:pages 與 containers 離開內部分層 ——
路由組合交給保留的 app 模組,模組根目錄佔 container 的位置 ——
其餘分層守則、規則、核心信念、元件軸線與工作指南全部不變:
// 以 --topology module-first 導入、經量測確認沒有原始碼的應用程式:
export default reactPreset({ name: 'web', modules: [] });nextPreset() 使用 React 語意,並依 app、pages 或 both 調整 layer-first 路由樹; router 預設為 app。srcDir: true 會選擇 src,否則原始碼根目錄為 .;別名預設為 @。由於 Server Component 原本就可能在路由樹各處取得資料,它不會將 fetch 限定在單一層。
從 3.2 升級到 4.0
有效的 3.2 設定仍是既有 layer-first 專案的權威。執行 4.0 init 會先完成支援的正規化與 修復。若同一次執行要求 module-first,Blueprint 會先把正規化後的 layer-first 設定寫成可 復原的檢查點,並停在此處。接著再次執行 init --topology module-first,才會從明確的 4.0 權威啟動受保護的轉換;不會在同一步裡重新解讀舊格式並搬移原始碼。
可執行的 mustNot
同一層可以混用既有文字與明確的可執行檢查。字串一律是 review only,由人與 Agent 審查;Blueprint 不會從文字、description、層名或檔名推導規則。React、Vue、Next 預設設定 中的字串維持原意,舊設定不需要改寫生命週期狀態。
mustNot: [
'change product policy',
{
id: 'render-ui',
description: 'render UI',
checks: [
{ capability: 'render-ui' },
{ package: 'leaflet' },
{ package: 'react-leaflet' },
{ package: 'react-leaflet-cluster' },
{ call: 'divIcon' },
],
exempt: ['src/domain/approved-adapter.ts'],
},
]同一項目的檢查採 OR 語意:任一檢查命中,就違反這項限制。id 必須符合 [a-z][a-z0-9-]*,且在該層中唯一;description、判別欄位的值與 checks 不可為空。 每個檢查只能使用 capability、package、global、call 其中一個判別欄位;未知欄位或 能力名稱會直接拒絕。正規化後重複的檢查,以及空白或重複的 imports 名稱也會拒絕。 exempt 使用可攜的專案相對 glob,在架構檔案範圍確定後套用到該項目的所有檢查。 輸出保留項目與檢查的宣告順序。
明確指定 Vue 的設定不能宣告 React 專屬能力,反之亦然。framework: 'auto' 則延後到 專案解析時確認相容性;無法確認必要的框架或 parser 支援時,回報 unavailable,不捏造 原始碼違規。直接呼叫 emitLint 的整合可以用 detectedFramework 提供 auto 的解析結果。
專案自訂檢查
| 檢查 | 精確範圍與限制 |
|---|---|
{ package: 'leaflet' } | 比對套件本身與 / 子路徑,不比對 leaflet-extra。@org/pkg 也不比對 @org/pkg-extra。這是套件家族比對,與 owns 的精確路徑語意不同。涵蓋靜態與 type-only 匯入/再匯出、可證明來源的 require(),以及能靜態化簡的動態匯入;執行期才確定的來源維持未驗證。 |
{ package: 'leaflet/*', pattern: true } | 使用既有 ownership/no-restricted-imports 群組比對語意,不額外展開套件家族。 |
{ package: 'react', imports: ['useState'] } | 比對靜態匯入與再匯出的具名匯出身分,包含別名與 type-only 形式。不推論 CommonJS/動態匯入的 namespace 成員、後續屬性存取、重新賦值或包裝函式;需要涵蓋這些情況時,選用完整套件、能力或 call 檢查。 |
{ global: 'WebSocket' } | 比對未被區域繫結遮蔽的值參照與支援的型別參照。宣告、屬性鍵、標籤、字面值與同名區域繫結合法。parser 無法提供必要參照空間時,覆蓋能力為 unavailable。 |
{ call: 'divIcon' } | 比對直接呼叫與末端成員名稱,包含 L.divIcon()、L['divIcon']()、可選呼叫,以及 JS/TS 與 SFC script。這是語法名稱比對,無關物件的同名方法也可能命中。不追蹤解構別名、包裝函式、重新賦值、執行期計算的名稱、建構子或 Vue template 呼叫。 |
能力偵測表
框架呼叫必須有可證明的匯入繫結,包含別名與 namespace 成員;無關的同名區域函式不會 命中。React 類別必須繼承可證明來自 React 的 Component 或 PureComponent。
| 能力 | 訊號 |
|---|---|
render-ui | JSX 元素/fragment;含可渲染元素、元件、插值或非空白文字的 Vue template;React createElement;Vue h/createVNode;React 類別的 render 方法。 |
react-state | React useState、useReducer、useSyncExternalStore;React 類別擁有 state 或使用 setState。 |
react-lifecycle | React useEffect、useLayoutEffect、useInsertionEffect,以及下列類別生命週期宣告。 |
vue-reactivity | Vue ref、shallowRef、reactive、shallowReactive、customRef、computed、toRef、toRefs。 |
vue-lifecycle | Vue onBeforeMount、onMounted、onBeforeUpdate、onUpdated、onBeforeUnmount、onUnmounted、onActivated、onDeactivated、onErrorCaptured、onRenderTracked、onRenderTriggered、onServerPrefetch。 |
routing | 可證明指向 react-router、react-router-dom、vue-router、next/router、next/navigation、next/link 的套件參照,包含子路徑與 type-only 參照。 |
network-io | 未被遮蔽的直接 fetch、XMLHttpRequest、WebSocket、EventSource 與 navigator.sendBeacon。 |
browser-dom | 未被遮蔽的直接 window、document、navigator、HTMLElement、Element、Node、EventTarget、CustomEvent、MutationObserver、ResizeObserver、IntersectionObserver、localStorage、sessionStorage、location、history、customElements、getComputedStyle。 |
React 類別生命週期宣告包含 componentDidMount、componentDidUpdate、componentWillUnmount、 shouldComponentUpdate、getSnapshotBeforeUpdate、getDerivedStateFromProps、 getDerivedStateFromError、componentDidCatch、componentWillMount、componentWillReceiveProps、 componentWillUpdate、UNSAFE_componentWillMount、UNSAFE_componentWillReceiveProps、 UNSAFE_componentWillUpdate。一般類別即使使用同名方法,仍是合法對照。
這些偵測器不證明透過包裝函式、相依注入、應用程式抽象、執行期行為或跨檔案值流造成的 間接行為。通過只表示實際檢查範圍內沒有觀察到已列明的訊號。
可用性、嚴重程度、串接與涵蓋範圍
Lint、Rules、Inspect、Doctor 與產出守則共用一份解析結果。字串為 review-only,沒有 機器載體。可執行項目在所有檢查可用時為 active,只有部分可用時為 partial,全部 不可用時為 unavailable;partial 項目中可用的檢查仍持續執行。可用性與 lint 串接狀態 (verified、unverified、reference-only)及檔案涵蓋狀態(covered、vacuous、 runway、unmeasured)彼此獨立。沒有原始碼的具體位置是 vacuous,modules: [] 是未來 模組的 runway,兩者都不代表已受行為保護。保留的 app 使用容器角色,不重複內層。
| 證據 | 關卡證據 | 退出與完成判定 |
|---|---|---|
emit.lint.severity: 'error' 的已證實命中 | failed | affectsExit: true;執行 lint 回傳非零,阻擋完成。 |
同一命中改為 warn | 可見但不阻擋的符合性觀察 | affectsExit: false;不改變彙總關卡,也不單獨造成非零退出。 |
| 必要偵測器 partial/unavailable | unverified,不捏造違規 | 不自動改變 CLI 退出碼;不論嚴重程度都阻擋完成。 |
| 已證實載體缺失、停用、替換或不相符 | failed | 沿用 Doctor 失敗行為;即使是 warn 也阻擋完成。 |
| 無法檢查實際生效設定 | unverified | 阻擋完成,但不自動造成非零退出。 |
| 檢查可用、載體完整、沒有 error 命中 | passed | 可同時有警告;仍須滿足其他完成條件。 |
混合情況保留 failed 與 unverified 各自的證據,彙總時 failed 優先。只有 lint 偵測原始碼 違規;Inspect 測量涵蓋範圍,不建立第二套 AST 偵測器,靜態 Rules 也不宣稱現場命中。 Doctor 用代表性或合成檔案驗證最後合併的 ESLint 設定,包括空位置。受管理的 blueprint/must-not 載體不能透過 lintOverrides 停用或替換。
先套用結構規則的 sourceRoot、層/模組 glob、框架副檔名、layerFilesIgnore、testFiles, 再套用項目豁免。本功能不新增 Inspect 退出碼政策。
