Merge pull request 'release: v0.0.6 develop 到 master' (#12) from develop into master

Reviewed-on: #12
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
This commit was merged in pull request #12.
This commit is contained in:
2026-08-27 09:01:20 +00:00
8 changed files with 289 additions and 11 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "jsc-review", "name": "jsc-review",
"version": "0.0.5", "version": "0.0.6",
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組", "description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組",
"skills": "./skills", "skills": "./skills",
"author": { "author": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "jsc-review", "name": "jsc-review",
"version": "0.0.5", "version": "0.0.6",
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組", "description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組",
"skills": "./skills" "skills": "./skills"
} }
+10
View File
@@ -28,6 +28,10 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
對 git diff 進行六組壞味道審查,每組一個 sub agent 平行執行;回報 `檔案:行號`、嚴重度、建議重構手法,修正與否由呼叫端決定。第 2 組同時擋「文件編號夾帶」:註解只寫「為什麼這樣寫」,議題編號、wiki 頁編號、工作包編號、commit hash、`@` 提及、外部文件連結一律不進註解,清單看 `references/comment-scope.md`。diff 是空的就直接回報「無發現」,不開任何 sub agent;六組全部回覆才進入彙整,沒東西可報的那組也要回「無發現」。安全性與 bug 審查交給 CLI 內建 review,不重複。 對 git diff 進行六組壞味道審查,每組一個 sub agent 平行執行;回報 `檔案:行號`、嚴重度、建議重構手法,修正與否由呼叫端決定。第 2 組同時擋「文件編號夾帶」:註解只寫「為什麼這樣寫」,議題編號、wiki 頁編號、工作包編號、commit hash、`@` 提及、外部文件連結一律不進註解,清單看 `references/comment-scope.md`。diff 是空的就直接回報「無發現」,不開任何 sub agent;六組全部回覆才進入彙整,沒東西可報的那組也要回「無發現」。安全性與 bug 審查交給 CLI 內建 review,不重複。
### `api-doc`
稽核 API 專案的 Swagger 文件:每個可能回傳的 HTTP 狀態碼都要宣告回覆類型,每個輸入與輸出都要有說明與真實資料範例,並沿著巢狀資料模型逐層遞迴。控制器改完或實作完成時執行,例如由 `jsc-sdlc:implement` 呼叫,與 `jsc-review:code-review` 並列為兩關收尾稽核。先跑 `tools/swagger-detect.sh` 偵測,套件與掛接設定要雙重命中才算支援;只裝套件沒掛接就回報未啟用並停手,不開任何 sub agent。原始碼的註解契約歸 `jsc-review:code-review` 第 5 組,這支只管 Swagger 文件屬性與範例,兩支不重複回報。回報 `檔案:行號`、嚴重度與建議修法,本技能不改程式碼。
<!-- JSC-SKILLS:END --> <!-- JSC-SKILLS:END -->
## 參考 ## 參考
@@ -37,6 +41,12 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
| `references/smells.md` | 六組壞味道完整清單:定義、偵測訊號、建議重構手法、嚴重度分級;範例資料必須去識別化 | | `references/smells.md` | 六組壞味道完整清單:定義、偵測訊號、建議重構手法、嚴重度分級;範例資料必須去識別化 |
| `references/comment-scope.md` | 程式碼註解內容界線:禁止寫進註解的文件編號清單、允許項目與白名單、命中時的改法 | | `references/comment-scope.md` | 程式碼註解內容界線:禁止寫進註解的文件編號清單、允許項目與白名單、命中時的改法 |
## 工具
| 檔案 | 用途 |
| --- | --- |
| `tools/swagger-detect.sh` | 判斷專案有沒有真的啟用 Swagger。套件與設定雙重確認,缺一不算支援。輸出 `support=`、`stack=`、`package=`、`config=`;結束碼 0 支援、1 不支援、2 參數或路徑錯誤 |
## 相關 domain ## 相關 domain
- [`jsc-sdlc`](https://gitea.jsc.idv.tw/plugins/sdlc):實作階段完成後呼叫本審查 - [`jsc-sdlc`](https://gitea.jsc.idv.tw/plugins/sdlc):實作階段完成後呼叫本審查
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "jsc-review", "name": "jsc-review",
"version": "0.0.5", "version": "0.0.6",
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組", "description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組",
"skills": "./skills/" "skills": "./skills/"
} }
+46 -7
View File
@@ -122,9 +122,9 @@
### 5.1 方法描述 ### 5.1 方法描述
- **定義**:每個公開方法要有一句話描述,並標明所屬層級:**顯示層、邏輯層、存取層**。 - **定義**:每個公開方法要有一句話描述,並標明所屬層級:**顯示層、邏輯層、存取層**。描述後面要接**條列式的處理步驟與規則**,讀註解就知道這個方法做了哪幾件事、依哪些規則做。
- **偵測訊號**:公開方法無描述;描述未標層級;描述與方法實際行為不符。 - **偵測訊號**:公開方法無描述;描述未標層級;描述與方法實際行為不符;描述只有一句話,方法內部卻有多段流程或多條判斷規則,註解裡找不到對應的條列;條列步驟與程式碼實際順序對不上。
- **建議重構手法**:補上單句描述與層級標記;若一個方法橫跨多層,先依 Split Phase 拆分再各自標註。 - **建議重構手法**:補上單句描述與層級標記,再把處理流程拆成條列步驟、把判斷條件寫成條列規則;若一個方法橫跨多層,先依 Split Phase 拆分再各自標註;條列長到十項以上,代表方法本身太肥,同時列 1.2 並建議 Extract Function。
### 5.2 輸入參數說明 ### 5.2 輸入參數說明
@@ -134,9 +134,9 @@
### 5.3 輸出說明 ### 5.3 輸出說明
- **定義**:輸出(回傳值)要說明回傳的資料內容概要。 - **定義**:輸出(回傳值)要說明回傳的資料內容概要。回傳值是自訂資料模型,而註解格式支援導向時(XML 的 `<see cref>`、JSDoc 的 `{@link}`),說明要附上該資料模型的檔案連結,讀的人一鍵就跳到定義。
- **偵測訊號**:無回傳說明;未說明 null、空集合、錯誤時的回傳;回傳布林但未說明 true/false 意義。 - **偵測訊號**:無回傳說明;未說明 null、空集合、錯誤時的回傳;回傳布林但未說明 true/false 意義;回傳自訂資料模型卻只寫型別名稱純文字,沒有 `<see cref>` 或 `{@link}` 導向。
- **建議重構手法**:補回傳內容概要與邊界情況說明。 - **建議重構手法**:補回傳內容概要與邊界情況說明;自訂資料模型改用註解格式的導向標籤指到型別定義,泛型集合指到元素型別。
### 5.4 輸入與輸出範例 ### 5.4 輸入與輸出範例
@@ -150,6 +150,35 @@
- **偵測訊號**:DTO/ViewModel 僅頂層有註解;內層類別、集合元素型別的欄位無說明或無範例。 - **偵測訊號**:DTO/ViewModel 僅頂層有註解;內層類別、集合元素型別的欄位無說明或無範例。
- **建議重構手法**:逐層補齊 5.1–5.4;巢狀過深(≥ 3 層)時同時評估 Extract Class 是否被濫用。 - **建議重構手法**:逐層補齊 5.1–5.4;巢狀過深(≥ 3 層)時同時評估 Extract Class 是否被濫用。
### 5.6 內含功能的導向連結
- **定義**:一個功能內部呼叫了其他功能時,註解要交代呼叫了誰、為什麼呼叫。註解格式支援導向就用導向標籤(XML 的 `<see cref>`、JSDoc 的 `{@link}`),讓 IDE 直接跳過去。
- **偵測訊號**:方法內呼叫其他公開方法或服務,註解卻完全沒提;提了但只寫純文字方法名,沒有 `<see cref>` 或 `{@link}`;寫了連結卻沒寫用途,讀的人還是要自己點進去猜。
- **建議重構手法**:在條列步驟裡把被呼叫的功能改成導向標籤,後面接一句用途;被呼叫的功能多到列不完,代表這個方法在當協調中心,同時列 1.2 或 3.1 評估職責搬移。
- **注意**:註解格式不支援導向(例如純 `//` 行註解)就只寫用途,不硬造連結字串。
### 5.7 XML 註解標籤排版
- **定義**:註解採 XML 格式時,起始標籤與結束標籤**各自獨立一行**,內容夾在中間。這樣多行內容、條列與巢狀標籤才排得整齊,diff 也只動到真正改的那幾行。
- **偵測訊號**:起始標籤、內容、結束標籤擠在同一行(例:`/// <summary>取得訂單</summary>`);結束標籤跟在內容尾巴後面沒有換行(例:`/// 取得訂單</summary>`);`<param>`、`<returns>`、`<remarks>`、`<example>` 同樣擠成一行;巢狀標籤(`<list>` 內含 `<item>`)沒有逐層換行與縮排。
- **建議重構手法**:把每個標籤拆成三行——起始標籤一行、內容一行或多行、結束標籤一行;巢狀標籤逐層縮排;`<param>` 這類帶屬性的標籤,屬性留在起始標籤那一行。
- **注意**:只針對 XML 格式註解。JSDoc、docstring 沒有結束標籤,不適用本項。
### 5.8 專有名詞與變數標示
- **定義**:註解裡提到程式碼元素時,要用該語言註解格式的標示語法標起來,不能混在純文字裡。標示的目的是讓 IDE 與文件產生工具讀得懂,並在文件上呈現成程式碼樣式。
- **偵測訊號**:XML 註解裡直接以純文字寫參數名、型別名、成員名或程式碼字面;JSDoc 或 docstring 裡以純文字寫變數名與型別名,沒有加反引號;標錯類別(例如拿 `<c>` 標參數名、拿 `<paramref>` 標型別)。
- **建議重構手法**:依語言慣例補標示。
| 註解格式 | 對象 | 標示語法 |
| --- | --- | --- |
| XML | 變數、參數 | `<paramref name="orderId" />` |
| XML | 型別、成員 | `<see cref="OrderService.GetOrder" />` |
| XML | 程式碼字面、列舉值 | `<c>null</c>` |
| JSDoc、docstring | 變數、型別、程式碼字面 | 反引號 `` `orderId` `` |
- **注意**:標示規則跟著語言慣例走,不是跟著個人喜好走。IDE 的跳轉與文件工具的交叉引用都靠這些標籤,標錯等於沒標。
## 第 6 組:淺模組(Shallow Module) ## 第 6 組:淺模組(Shallow Module)
- **定義**:介面複雜度相對於功能深度過高的模組——使用它要懂的事,跟自己寫差不多(出自《A Philosophy of Software Design》:好模組要「介面簡單、實作深」)。 - **定義**:介面複雜度相對於功能深度過高的模組——使用它要懂的事,跟自己寫差不多(出自《A Philosophy of Software Design》:好模組要「介面簡單、實作深」)。
@@ -162,4 +191,14 @@
| --- | --- | --- | | --- | --- | --- |
| 高 | 會造成錯誤或已阻礙修改 | 吞掉異常、重複程式碼改漏、死碼誤導 | | 高 | 會造成錯誤或已阻礙修改 | 吞掉異常、重複程式碼改漏、死碼誤導 |
| 中 | 持續增加維護成本 | 巨型類別、臃腫函式、巢狀地獄、Couplers 全組 | | 中 | 持續增加維護成本 | 巨型類別、臃腫函式、巢狀地獄、Couplers 全組 |
| 低 | 可讀性與一致性 | 命名、魔術數字、註解缺漏、文件編號夾帶、淺模組 | | 低 | 可讀性與一致性 | 命名、魔術數字、註解缺漏、文件編號夾帶、淺模組、XML 標籤排版、專有名詞未標示 |
第 5 組新增項目的級別另外標明,避免全部壓在「註解缺漏」一句話裡:
| 項目 | 級別 | 理由 |
| --- | --- | --- |
| 5.1 缺條列式步驟與規則 | 中 | 讀的人要重讀整個實作才知道規則,維護成本直接上升 |
| 5.3 回傳值缺資料模型連結 | 低 | 型別名稱還查得到,只是多花幾秒 |
| 5.6 缺內含功能的導向連結 | 低 | 同上,影響的是查找速度 |
| 5.7 XML 標籤排版擠在同一行 | 低 | 純排版,不影響語意 |
| 5.8 專有名詞未標示或標錯 | 低 | 影響 IDE 跳轉與文件呈現,不影響執行 |
+58
View File
@@ -0,0 +1,58 @@
---
name: api-doc
description: Audit an API project's Swagger/OpenAPI documentation: every returnable HTTP status code declares a response type, and every input and output carries a description plus a real data example, recursively down nested data models. Run after controller work or an implementation is complete, e.g. from jsc-sdlc implement, next to jsc-review:code-review. Detection runs first via tools/swagger-detect.sh; without both the Swagger package and its wiring, report unsupported and stop. The source comment contract belongs to jsc-review:code-review group 5; this skill covers Swagger document attributes only. Findings carry file:line, severity, and the fix; this skill never modifies code.
---
# api-doc
Audit whether an API project's Swagger (OpenAPI) documentation is complete enough for a caller to integrate against it without reading the implementation.
## When to run
1. **After controller work is complete**: one controller or one related group of controllers is done.
2. **When an implementation is complete**: all todos of a work package that touched API endpoints are done. This is the call site in `jsc-sdlc:implement`, right next to `jsc-review:code-review`.
3. **Never on a project without Swagger**: step 1 below decides this in code, not by judgment.
## Division of labor
- This skill covers **Swagger document attributes only**: response type declarations, Swagger parameter descriptions, and Swagger examples.
- The comment contract — method description, layer tag, parameter description, return description, examples, nested-structure comments — belongs to `jsc-review:code-review` group 5 (`references/smells.md` 5.1 to 5.8). Never report the same gap twice; when a nested model already fails 5.5, leave it to `code-review`.
- Security, logic bugs, and test coverage belong to the CLI's built-in review.
## Steps
1. Detect Swagger support: run `tools/swagger-detect.sh {project path}` (defaults to the current directory). The script confirms package **and** wiring, so an installed-but-never-enabled project comes back unsupported. Read its exit code: `0` supported, `1` unsupported, `2` bad argument or missing path. Completion condition: the exit code and the `support=`, `stack=`, `package=`, `config=` lines are captured.
2. On exit code `1`, report the literal 「本專案未啟用 Swagger 文件,略過 API 文件稽核」 plus the `stack=` and `package=` lines the script printed, and stop. Spawn no sub agent. On exit code `2`, report the script's error message verbatim and stop; the caller supplies a valid project path and re-runs. Completion condition: the run has ended with an honest reason, or exit code `0` moved it to step 3.
3. List the audit scope: every controller in the project, or only the controllers touched by `git diff` when the caller asked for a scoped run. Completion condition: the controller list is non-empty and reported; an empty list ends the run with the literal 「無發現」.
4. Audit in three aspects, and every aspect **MUST run as a sub agent**; the three may run in parallel:
| Aspect | Scope |
| --- | --- |
| 1 Status codes | For every action, every HTTP status code it can actually return — success, validation failure, authorization failure, not found, server error — has a declared response type and body schema (`ProducesResponseType`, `@ApiResponse`, FastAPI `responses=`, drf-spectacular `@extend_schema`). A status code the code can produce but the document never declares is a finding; so is a declared status code the code can never produce |
| 2 Parameter description and example | Every input parameter and every output field has a description and an example. Examples come from real project data first — query the project's database, seed data, or fixtures. Only when real data is unreachable, derive an example by logic and mark it as a derived value in the document itself |
| 3 Recursive data model | When a parameter or return value is a data model, aspect 2 applies to every one of its properties. A model containing another model recurses to the innermost layer. Walk in from the controller and follow the input and output types down |
Instructions for each sub agent: read only, change nothing; report each finding as `file:line`, the aspect, severity, one sentence of evidence, and the concrete fix (which attribute to add, on which member). Findings are reported in Traditional Chinese.
Completion condition: all three aspects have returned — an aspect with nothing to report still returns 「無發現」 for itself.
5. Merge the three aspects' findings: deduplicate by location, then sort by severity. Start this step only once all three have returned. Completion condition: every finding appears exactly once, ordered 高 → 中 → 低.
6. Report the finding list. **This skill never modifies code**; the caller decides what to fix. Completion condition: the report is handed to the caller and the fix decision is left to them.
## Severity
The 高、中、低 scale is the one in `references/smells.md`. Map this skill's findings onto it:
| Finding | Severity |
| --- | --- |
| A returnable status code has no declared response type | 高 |
| A declared response type does not match the body the code returns | 高 |
| An input or output has no description | 中 |
| A data model property is missing from the recursive walk entirely | 中 |
| A placeholder example while real data was reachable | 低 |
| A derived example that is not marked as derived | 低 |
## Notes
- Examples taken from a database must be de-identified. Never put personal data into a Swagger document.
- 「推導值」 is the literal marker for a derived example; keep it in the document text so the next reader knows the value was never observed.
- When there are no findings, report the literal 「無發現」 explicitly; never leave the report empty.
+2 -1
View File
@@ -16,6 +16,7 @@ Review changed code against `references/smells.md` (from the book *Refactoring*)
- This skill covers the *Refactoring* smells, the comment contract (group 5), and shallow modules (group 6). - This skill covers the *Refactoring* smells, the comment contract (group 5), and shallow modules (group 6).
- Security, logic bugs, and test coverage belong to the CLI's built-in review (e.g. claude's `/security-review`); do not duplicate them. - Security, logic bugs, and test coverage belong to the CLI's built-in review (e.g. claude's `/security-review`); do not duplicate them.
- Swagger document auditing belongs to `jsc-review:api-doc`: response type declarations per HTTP status code, Swagger parameter descriptions, and Swagger examples down the nested data models. Group 5 here covers the source comment contract only; the two never report the same gap twice.
## Steps ## Steps
@@ -28,7 +29,7 @@ Review changed code against `references/smells.md` (from the book *Refactoring*)
| 2 Obscurity | smells.md group 2, including 2.5 document reference leak — comments carrying issue ids, wiki page ids, work package ids, commit hashes, @ mentions, or external document links; the full banned and allowed lists live in `references/comment-scope.md` | | 2 Obscurity | smells.md group 2, including 2.5 document reference leak — comments carrying issue ids, wiki page ids, work package ids, commit hashes, @ mentions, or external document links; the full banned and allowed lists live in `references/comment-scope.md` |
| 3 Couplers | smells.md group 3 | | 3 Couplers | smells.md group 3 |
| 4 Dispensables & Others | smells.md group 4 | | 4 Dispensables & Others | smells.md group 4 |
| 5 Comment contract | smells.md group 5 | | 5 Comment contract | smells.md group 5, 5.1 to 5.8 — including 5.6 navigation links to the functions a method calls, 5.7 XML comment tag layout (opening and closing tag each on its own line), and 5.8 code-element markup by language convention (`<paramref>`, `<see cref>`, `<c>` in XML; backticks in JSDoc and docstrings) |
| 6 Shallow Module | smells.md group 6 | | 6 Shallow Module | smells.md group 6 |
Instructions for each sub agent: read only, change nothing; check every changed line and its enclosing function or class against the group's definitions and detection signals in smells.md; report each finding as `file:line`, smell name, severity (高、中、低 per the smells.md scale), one sentence of evidence, and the suggested refactoring. Findings are reported in Traditional Chinese. Instructions for each sub agent: read only, change nothing; check every changed line and its enclosing function or class against the group's definitions and detection signals in smells.md; report each finding as `file:line`, smell name, severity (高、中、低 per the smells.md scale), one sentence of evidence, and the suggested refactoring. Findings are reported in Traditional Chinese.
+170
View File
@@ -0,0 +1,170 @@
#!/usr/bin/env sh
# swagger-detect.sh — 判斷一個專案有沒有真的啟用 Swagger(OpenAPI)文件。
# 用法:swagger-detect.sh [專案路徑](預設目前目錄)
#
# 規則(雙重確認,缺一不算支援):
# 1. 套件:在套件宣告檔裡找得到已知的 Swagger 套件。
# 2. 設定:在原始碼裡找得到實際把 Swagger 接上去的呼叫或裝飾子。
# 只找到套件不算支援。裝了沒啟用的專案很常見,只看套件會誤判,
# 讓 api-doc 稽核跑在一個根本產不出文件的專案上。
# 設定關鍵字一律挑「接線動作」,不挑 import 或 require。光是引入套件
# 不代表文件真的掛上去了。
#
# 涵蓋範圍:
# dotnet 套件 Swashbuckle.AspNetCore、NSwag.AspNetCore
# 設定 AddSwaggerGen、UseSwagger、AddOpenApiDocument、UseOpenApi
# nodejs 套件 swagger-ui-express、@nestjs/swagger、fastify-swagger(含 @fastify/swagger)
# 設定 SwaggerModule.setup、swaggerUi.setup、swaggerUi.serve、
# register 進 fastify 的 swagger 外掛
# python 套件 fastapi、flasgger、drf-spectacular
# 設定 FastAPI( 建立 app、Swagger( 掛上 flasgger、SPECTACULAR_SETTINGS、
# SpectacularAPIView
#
# 輸出(純文字,一行一個 key=value,呼叫端逐行讀就好):
# 第一行永遠是 support=yes 或 support=no。
# 之後每一組偵測到套件的技術棧輸出三行:
# stack=dotnet|nodejs|python
# package=命中的套件名,多個以半形逗號相連
# config=命中的設定關鍵字,多個以半形逗號相連;沒命中就是空字串
# 一個套件都沒找到時只有 support=no 一行。
# 範例:
# support=yes
# stack=dotnet
# package=Swashbuckle.AspNetCore
# config=AddSwaggerGen,UseSwagger
#
# 結束碼:
# 0 支援(至少一個技術棧同時命中套件與設定)
# 1 不支援(沒有任何技術棧同時命中)
# 2 參數個數不對、專案路徑不存在,或環境缺 grep
#
# 護欄:
# 掃描一律跳過 node_modules、.git、bin、obj、dist、build、venv、.venv、
# __pycache__、packages、vendor,避免把相依套件自己的原始碼當成專案設定。
# 錯誤訊息一律印繁中到 stderr,正常輸出只走 stdout。
set -u
if [ "$#" -gt 1 ]; then
echo "用法:swagger-detect.sh [專案路徑]" >&2
exit 2
fi
DIR="${1:-.}"
[ -d "$DIR" ] || { echo "錯誤:找不到專案路徑 $DIR。請確認路徑後重試。" >&2; exit 2; }
command -v grep >/dev/null 2>&1 || { echo "錯誤:環境缺 grep,無法掃描。" >&2; exit 2; }
EX1=--exclude-dir=node_modules
EX2=--exclude-dir=.git
EX3=--exclude-dir=bin
EX4=--exclude-dir=obj
EX5=--exclude-dir=dist
EX6=--exclude-dir=build
EX7=--exclude-dir=venv
EX8=--exclude-dir=.venv
EX9=--exclude-dir=__pycache__
EX10=--exclude-dir=packages
EX11=--exclude-dir=vendor
# scan <延伸正規表示式> <副檔名樣式…>:命中回 0,沒命中回 1。
scan() {
pattern="$1"
shift
for inc in "$@"; do
if grep -R -l -E -i \
"$EX1" "$EX2" "$EX3" "$EX4" "$EX5" "$EX6" \
"$EX7" "$EX8" "$EX9" "$EX10" "$EX11" \
--include="$inc" -e "$pattern" "$DIR" >/dev/null 2>&1; then
return 0
fi
done
return 1
}
# append <既有清單> <新項目>:以半形逗號相連後印出。
append() {
if [ -z "$1" ]; then
printf '%s' "$2"
else
printf '%s,%s' "$1" "$2"
fi
}
SUPPORT=no
RECORDS=""
# collect <技術棧> <套件清單> <設定清單>:有套件才留紀錄,兩者都有才算支援。
collect() {
[ -n "$2" ] || return 0
RECORDS="${RECORDS}stack=$1
package=$2
config=$3
"
[ -n "$3" ] && SUPPORT=yes
return 0
}
# ── dotnet ────────────────────────────────────────────────────────────────
DOTNET_PKG=""
DOTNET_CFG=""
for pkg in 'Swashbuckle\.AspNetCore:Swashbuckle.AspNetCore' 'NSwag\.AspNetCore:NSwag.AspNetCore'; do
if scan "${pkg%%:*}" '*.csproj' '*.fsproj' '*.vbproj' '*.props' 'packages.config'; then
DOTNET_PKG=$(append "$DOTNET_PKG" "${pkg##*:}")
fi
done
if [ -n "$DOTNET_PKG" ]; then
for cfg in AddSwaggerGen UseSwagger AddOpenApiDocument UseOpenApi; do
if scan "$cfg" '*.cs' '*.fs' '*.vb'; then
DOTNET_CFG=$(append "$DOTNET_CFG" "$cfg")
fi
done
fi
collect dotnet "$DOTNET_PKG" "$DOTNET_CFG"
# ── nodejs ────────────────────────────────────────────────────────────────
NODE_PKG=""
NODE_CFG=""
for pkg in swagger-ui-express @nestjs/swagger fastify-swagger @fastify/swagger; do
if scan "\"$pkg\"" 'package.json'; then
NODE_PKG=$(append "$NODE_PKG" "$pkg")
fi
done
if [ -n "$NODE_PKG" ]; then
# 每一項都是「接線動作」:掛路由或註冊外掛,不是 import。
for cfg in 'SwaggerModule\.setup:SwaggerModule.setup' \
'swaggerUi\.setup:swaggerUi.setup' \
'swaggerUi\.serve:swaggerUi.serve' \
'register\(.*swagger:register(swagger)'; do
if scan "${cfg%%:*}" '*.js' '*.mjs' '*.cjs' '*.ts'; then
NODE_CFG=$(append "$NODE_CFG" "${cfg##*:}")
fi
done
fi
collect nodejs "$NODE_PKG" "$NODE_CFG"
# ── python ────────────────────────────────────────────────────────────────
PY_PKG=""
PY_CFG=""
for pkg in fastapi flasgger drf-spectacular; do
if scan "$pkg" 'requirements*.txt' 'pyproject.toml' 'Pipfile' 'setup.py' 'setup.cfg'; then
PY_PKG=$(append "$PY_PKG" "$pkg")
fi
done
if [ -n "$PY_PKG" ]; then
for cfg in 'FastAPI\(:FastAPI(' \
'Swagger\(:Swagger(' \
'SPECTACULAR_SETTINGS:SPECTACULAR_SETTINGS' \
'SpectacularAPIView:SpectacularAPIView'; do
if scan "${cfg%%:*}" '*.py'; then
PY_CFG=$(append "$PY_CFG" "${cfg##*:}")
fi
done
fi
collect python "$PY_PKG" "$PY_CFG"
# ── 輸出 ──────────────────────────────────────────────────────────────────
echo "support=$SUPPORT"
[ -n "$RECORDS" ] && printf '%s' "$RECORDS"
[ "$SUPPORT" = yes ] && exit 0
exit 1