configlet 最主要的用途是 Lint 檢查:檢查某個 track 的設定檔結構是否正確,包括語法與語意兩方面。設定有誤的 track 可能無法正確同步、在網站上顯示異常,或帶給使用者不佳的體驗,因此 configlet 的檢查機制對於維護 Exercism 的完整性扮演著重要角色。
你可以使用 lint 指令來檢查一個 track。
configlet [global-options] lint [command-options]
Global options:
-h, --help Show this help message and exit
--version Show this tool's version information and exit
-t, --track-dir <dir> Specify a track directory to use instead of the current directory
-v, --verbosity <verbosity> The verbosity of output. Allowed values: q[uiet], n[ormal], d[etailed]
以下是會檢查的 Lint 規則。
Lint 工具應檢查是否所有必備檔案都存在。不屬於個別練習的必備檔案包括:
config.jsondocs/ABOUT.mddocs/INSTALLATION.mddocs/LEARNING.mddocs/RESOURCES.mddocs/SNIPPET.txtdocs/TESTS.mdexercises/shared/.docs/help.mdexercises/shared/.docs/tests.md概念練習專屬的必備檔案包括:
exercises/concept/<slug>/.docs/hints.mdexercises/concept/<slug>/.docs/instructions.mdexercises/concept/<slug>/.docs/introduction.mdexercises/concept/<slug>/.meta/config.json練習題也會有類似的清單,但我們尚未定義相關規格。
config.json 中列出的每個概念都應包含以下檔案:
concepts/<slug>/about.mdconcepts/<slug>/introduction.mdconcepts/<slug>/links.jsonconfig.json 檔應接受以下檢查:
"language" 鍵為必填"language" 值必須是長度 <= 255 的非空白字串¹"slug" 鍵為必填"slug" 值必須是長度 <= 255 的 kebab-case 字串²"active" 鍵為必填"active" 值必須是布林值"blurb" 鍵為必填"blurb" 值必須是長度 <= 400 的非空白字串¹"version" 鍵為必填"version" 值必須是整數 3
"status.concept_exercises" 鍵為必填"status.concept_exercises" 值必須是布林值"status.test_runner" 鍵為必填"status.test_runner" 值必須是布林值"status.representer" 鍵為必填"status.representer" 值必須是布林值"status.analyzer" 鍵為必填"status.analyzer" 值必須是布林值"online_editor.indent_style" 鍵為必填"online_editor.indent_style" 值必須是字串 space 或 tab
"online_editor.indent_size" 鍵為必填"online_editor.indent_size" 值必須是 >= 0 且 <= 8 的整數"online_editor.highlightjs_language" 鍵為選填"online_editor.highlightjs_language" 值必須是非空白字串¹"files" 鍵為選填"files" 值必須是物件"files.solution" 鍵為選填"files.solution" 值必須是陣列"files.solution" 值必須是有效的模式⁵"files.solution" 值不得有重複"files.test" 鍵為選填"files.test" 值必須是陣列"files.test" 值必須是有效的模式⁵"files.test" 值不得有重複"files.example" 鍵為選填"files.example" 值必須是陣列"files.example" 值必須是有效的模式⁵"files.example" 值不得有重複"files.exemplar" 鍵為選填"files.exemplar" 值必須是陣列"files.exemplar" 值必須是有效的模式⁵"files.exemplar" 值不得有重複"files.editor" 鍵為選填"files.editor" 值必須是陣列"files.editor" 值必須是有效的模式⁵"files.editor" 值不得有重複"files.invalidator" 鍵為選填"files.invalidator" 值必須是陣列"files.invalidator" 值必須是有效的模式⁵"files.invalidator" 值不得有重複"files.solution"、"files.test"、"files.example"、"files.exemplar"、"files.editor" 或 "files.invalidator"(不得重疊)
d 或 plsql,則 "files.solution" 與 "files.test" 的檔案_可以_重疊"files.example 與 "files.exemplar" 的檔案_可以_重疊"status.test_runner" 等於 true,則 "test_runner.average_run_time" 鍵為必填"test_runner.average_run_time" 值必須是 > 0 的整數"approaches.snippet_extension" 鍵為必填"approaches.snippet_extension" 值必須是非空白字串¹"exercises" 鍵為必填"exercises.concept" 鍵為必填"exercises.concept" 值必須是陣列"exercises.concept[].slug" 鍵為必填"exercises.concept[].slug" 值必須是長度 <= 255 的 kebab-case 字串²"exercises.concept[].slug" 值在 "exercises.concept[].slug" 中必須唯一,且不得存在於 "exercises.practice[].slug" 中"exercises.concept[].name" 鍵為必填"exercises.concept[].name" 值必須是長度 <= 255 的 Title Case 字串³"exercises.concept[].uuid" 鍵為必填"exercises.concept[].uuid" 值必須是唯一的 version 4 UUID 字串⁶"exercises.concept[].uuid" 值永遠不得變更"exercises.concept[].concepts" 鍵為必填"exercises.concept[].status" 不等於 deprecated,則 "exercises.concept[].concepts" 值必須是非空的字串陣列"exercises.concept[].status" 等於 deprecated,則 "exercises.concept[].concepts" 值必須是空陣列"exercises.concept[].concepts" 值必須是 kebab-case 字串²"exercises.concept[].concepts" 值不得有重複"exercises.concept[].concepts" 值不得出現在任何其他概念練習的 "concepts" 屬性中"exercises.concept[].concepts" 值必須符合其中一個概念的 "concepts.slug" 屬性"exercises.concept[].prerequisites" 鍵為必填"exercises.concept[].status" 不等於 deprecated,則 "exercises.concept[].prerequisites" 值必須是非空的字串陣列,但恰好有一個練習例外,其值_可以_是空陣列"exercises.concept[].status" 等於 deprecated,則 "exercises.concept[].prerequisites" 值必須是空陣列"exercises.concept[].prerequisites" 值必須是 kebab-case 字串²"exercises.concept[].prerequisites" 值不得有重複"exercises.concept[].prerequisites" 值必須符合任何其他概念練習的 "concepts" 屬性值"exercises.concept[].prerequisites" 值不得符合該練習 "exercises.concept[].concepts" 屬性中的任何值"exercises.concept[].prerequisites" 值必須符合其中一個概念的 "concepts.slug" 屬性"exercises.concept[].concepts" 與 "exercises.concept[].prerequisites" 之間不得有任何循環"exercises.concept[].status" 鍵為選填"exercises.concept[].status" 值必須是字串 wip、beta、active 或 deprecated
"exercises.practice" 鍵為必填"exercises.practice" 值必須是陣列"exercises.practice[].slug" 鍵為必填"exercises.practice[].slug" 值必須是長度 <= 255 的 kebab-case 字串²"exercises.practice[].slug" 值在 "exercises.practice[].slug" 中必須唯一,且不得存在於 "exercises.concept[].slug" 中"exercises.practice[].slug" 值為字串 hello-world
"exercises.practice[].name" 鍵為必填"exercises.practice[].name" 值必須是長度 <= 255 的 Title Case 字串³"exercises.practice[].uuid" 鍵為必填"exercises.practice[].uuid" 值必須是唯一的 version 4 UUID 字串⁶"exercises.practice[].uuid" 值永遠不得變更"exercises.practice[].difficulty" 鍵為必填"exercises.practice[].difficulty" 值必須是 >= 1 且 <= 10 的整數"exercises.practice[].practices" 鍵為必填"exercises.practice[].status" 不等於 deprecated,則 "exercises.practice[].practices" 值必須是非空的字串陣列"exercises.practice[].status" 等於 deprecated,則 "exercises.practice[].practices" 值必須是空陣列"exercises.practice[].practices" 值必須是 kebab-case 字串²"exercises.practice[].practices" 值不得有重複"exercises.practice[].practices" 值必須符合其中一個概念的 "concepts[].slug" 屬性"exercises.practice[].practices" 值對個別概念的 slug 最多只能引用 10 次(跨所有練習)"exercises.practice[].prerequisites" 鍵為必填"exercises.practice[].status" 不等於 deprecated,則 "exercises.practice[].prerequisites" 值必須是非空的字串陣列"exercises.practice[].status" 等於 deprecated,則 "exercises.practice[].prerequisites" 值必須是空陣列"exercises.practice[].slug" 等於 hello-world,則 "exercises.practice[].prerequisites" 值必須是空陣列"exercises.practice[].prerequisites" 值必須是 kebab-case 字串²"exercises.practice[].prerequisites" 值不得有重複"exercises.practice[].prerequisites" 值必須符合任何概念練習的 "exercises.concept[].concepts" 值"exercises.practice[].prerequisites" 值必須符合其中一個概念的 "concepts[].slug" 屬性"exercises.practice[].status" 鍵為選填"exercises.practice[].status" 值必須是字串 wip、beta、active 或 deprecated
"exercises.practice[].slug" 等於 hello-world,則 "exercises.practice[].status" 值必須省略,或是字串 active
"exercises.foregone" 鍵為選填"exercises.foregone" 值必須是陣列"exercises.foregone" 值必須是 kebab-case 字串²"exercises.foregone" 值不得有重複"exercises.foregone" 值不得符合任何概念或練習題的 slug"concepts" 鍵為必填"concepts" 值必須是陣列"concepts[].uuid" 鍵為必填"concepts[].uuid" 值必須是唯一的 version 4 UUID 字串⁶"concepts[].uuid" 值永遠不得變更"concepts[].slug" 鍵為必填"concepts[].slug" 值必須是長度 <= 255 的 kebab-case 字串²"concepts[].name" 鍵為必填"concepts[].name" 值必須是長度 <= 255 的 Title Case 字串³"concepts[].tags" 鍵為選填"concepts[].tags.all" 鍵為選填,除非 "concepts[].tags.any" 為空"concepts[].tags.all" 值必須是陣列"concepts[].tags.all" 值必須是長度 <= 255 的非空白標籤⁷字串"concepts[].tags.all" 值不得有重複"concepts[].tags.any" 鍵為選填,除非 "concepts[].tags.all" 為空"concepts[].tags.any" 值必須是陣列"concepts[].tags.any" 值必須是長度 <= 255 的非空白標籤⁷字串"concepts[].tags.any" 值不得有重複"concepts[].tags.not" 鍵為選填"concepts[].tags.not" 值必須是陣列"concepts[].tags.not" 值必須是長度 <= 255 的非空白標籤⁷字串"concepts[].tags.not" 值不得有重複"concepts" 值都必須有一個 concept/<concepts.slug>/about.md 檔案。此檔案的 Lint 規則詳述於下。"concepts" 值都必須有一個 concept/<concepts.slug>/introduction.md 檔案。此檔案的 Lint 規則詳述於下。"concepts" 值都必須有一個 concept/<concepts.slug>/links.json 檔案。此檔案的 Lint 規則詳述於下。"key_features" 鍵為選填"key_features" 值必須是長度為 6 的陣列"key_features[].icon" 鍵為必填"key_features[].icon" 值必須使用其中一個預先定義的圖示值
"key_features[].title" 鍵為必填"key_features[].title" 值必須是長度 <= 25 的 Sentence Case 字串⁴"key_features[].content" 鍵為必填"key_features[].content" 值必須是長度 <= 100 的非空白字串¹"tags" 鍵為必填"tags" 值必須是字串陣列"tags" 值不得有重複"tags" 值必須使用其中一個預先定義的標籤值
"blurb" 鍵為必填"blurb" 值必須是長度 <= 350 的非空白字串¹"source" 鍵為選填"source" 值必須是非空白字串¹"source_url" 鍵為選填"source_url" 值必須是 URL"authors" 鍵為必填"authors" 值必須是非空陣列"authors" 值必須是非空白字串¹"authors" 值不得有重複"authors" 值以不分大小寫的方式處理"contributors" 鍵為選填"contributors" 值必須是陣列"contributors" 值必須是非空白字串¹"contributors" 值不得有重複"contributors" 值以不分大小寫的方式處理"authors" 或 "contributors" 其中一個陣列中(不得重疊)"files.solution" 鍵為必填"files.solution" 值必須是非空陣列"files.solution" 值不得有重複"files.test" 鍵為必填"files.test" 值必須是非空陣列"files.test" 值不得有重複"files.exemplar" 鍵為必填"files.exemplar" 值必須是非空陣列"files.exemplar" 值不得有重複"files.editor" 鍵為選填"files.editor" 值必須是陣列"files.editor" 值不得有重複"files.invalidator" 鍵為選填"files.invalidator" 值必須是陣列"files.invalidator" 值不得有重複"files.solution" 中列出的檔案必須存在"files.test" 中列出的檔案必須存在"files.exemplar" 中列出的檔案必須存在"files.editor" 中列出的檔案必須存在"files.invalidator" 中列出的檔案必須存在"files.solution"、"files.test"、"files.exemplar" 或 "files.invalidator"(不得重疊)
d 或 plsql,則 "files.solution" 與 "files.test" 的檔案_可以_重疊"forked_from" 鍵為選填"forked_from" 值必須是陣列"forked_from" 值必須是格式為 <track-slug>/<exercise-slug> 的字串(例如 fsharp/bird-watcher)"forked_from" 值必須指向實際已實作的練習"forked_from" 值不得有重複"language_versions" 鍵為選填"language_versions" 值必須是字串"representer.version" 鍵為選填"representer.version" 值必須是 >= 1 的整數"icon" 鍵為選填"icon" 值必須是 kebab-case 字串²## General 或 ## X. <task>,其中 X 對應 instructions.md 中的任務編號標題## 1. Do X
config.json 中其中一個概念的 "concepts.slug" 屬性。"blurb" 鍵為必填"blurb" 值必須是長度 <= 350 的非空白字串¹"source" 鍵為選填"source" 值必須是非空白字串¹"source_url" 鍵為選填"source_url" 值必須是 URL"authors" 鍵為選填"authors" 值必須是陣列"authors" 值必須是非空白字串¹"authors" 值不得有重複"authors" 值以不分大小寫的方式處理"contributors" 鍵為選填"contributors" 值必須是陣列"contributors" 值必須是非空白字串¹"contributors" 值不得有重複"contributors" 值以不分大小寫的方式處理"authors" 或 "contributors" 其中一個陣列中(不得重疊)"files.solution" 鍵為必填"files.solution" 值必須是非空陣列"files.solution" 值不得有重複"files.test" 鍵為必填"files.test" 值必須是非空陣列"files.test" 值不得有重複"files.example" 鍵為必填"files.example" 值必須是非空陣列"files.example" 值不得有重複"files.editor" 鍵為選填"files.editor" 值必須是陣列"files.editor" 值不得有重複"files.invalidator" 鍵為選填"files.invalidator" 值必須是陣列"files.invalidator" 值不得有重複"files.solution" 中列出的檔案必須存在"files.test" 中列出的檔案必須存在"files.example" 中列出的檔案必須存在"files.editor" 中列出的檔案必須存在"files.invalidator" 中列出的檔案必須存在"files.solution"、"files.test"、"files.example 或 "files.invalidator"(不得重疊)
d 或 plsql,則 "files.solution" 與 "files.test" 的檔案_可以_重疊"language_versions" 鍵為選填"language_versions" 值必須是字串"test_runner" 鍵為選填"test_runner" 值必須是布林值"representer.version" 鍵為選填"representer.version" 值必須是 >= 1 的整數"icon" 鍵為選填"icon" 值必須是 kebab-case 字串²introduction.md 或同層目錄"introduction.authors" 鍵為選填"introduction.authors" 值必須是陣列"introduction.authors" 值必須是非空白字串¹"introduction.authors" 值不得有重複"introduction.authors" 值以不分大小寫的方式處理"introduction.authors" 陣列非空,則必須有一個非空的 introduction.md 檔案"introduction.contributors" 鍵為選填"introduction.contributors" 值必須是陣列"introduction.contributors" 值必須是非空白字串¹"introduction.contributors" 值不得有重複"introduction.contributors" 值以不分大小寫的方式處理"introduction.contributors" 陣列非空,則必須有一個非空的 introduction.md 檔案"introduction.authors" 或 "introduction.contributors" 其中一個陣列中(不得重疊)"approaches" 鍵為選填,除非存在同層目錄(其中包含該 approach 的檔案)"approaches" 值必須是物件陣列"approaches[].uuid" 鍵為必填"approaches[].uuid" 值必須是唯一的 version 4 UUID 字串⁶"approaches[].uuid" 值永遠不得變更"approaches[].slug" 鍵為必填"approaches[].slug" 值必須是長度 <= 255 的 kebab-case 字串²"approaches[].slug" 值必須有對應的非空 <slug>/content.md 檔案"approaches[].slug" 值必須有對應的非空 <slug>/snippet.txt 檔案"approaches[].title" 鍵為必填"approaches[].title" 值必須是長度 <= 255 的 Title Case 字串³"approaches[].blurb" 鍵為必填"approaches[].blurb" 值必須是長度 <= 350 的非空白字串¹"approaches[].authors" 鍵為必填"approaches[].authors" 值必須是非空陣列"approaches[].authors" 值必須是非空白字串¹"approaches[].authors" 值不得有重複"approaches[].authors" 值以不分大小寫的方式處理"approaches[].contributors" 鍵為選填"approaches[].contributors" 值必須是陣列"approaches[].contributors" 值必須是非空白字串¹"approaches[].contributors" 值不得有重複"approaches[].contributors" 值以不分大小寫的方式處理"approaches[].authors" 或 "approaches[].contributors" 其中一個陣列中(不得重疊)"approaches[].tags" 鍵為選填"approaches[].tags.all" 鍵為選填,除非 "approaches[].tags.any" 為空"approaches[].tags.all" 值必須是陣列"approaches[].tags.all" 值必須是長度 <= 255 的非空白標籤⁷字串"approaches[].tags.all" 值不得有重複"approaches[].tags.any" 鍵為選填,除非 "approaches[].tags.all" 為空"approaches[].tags.any" 值必須是陣列"approaches[].tags.any" 值必須是長度 <= 255 的非空白標籤⁷字串"approaches[].tags.any" 值不得有重複"approaches[].tags.not" 鍵為選填"approaches[].tags.not" 值必須是陣列"approaches[].tags.not" 值必須是長度 <= 255 的非空白標籤⁷字串"approaches[].tags.not" 值不得有重複.approaches/config.json 檔中存在對應的 "approaches[].slug" 項目,則此檔案為必填.approaches/config.json 檔中存在對應的 "approaches[].slug" 項目,則此檔案為必填config.json 檔中的 approaches.snippet_extension 值"articles" 鍵為選填,除非存在同層目錄(其中包含該 article 的檔案)"articles" 值必須是物件陣列"articles[].uuid" 鍵為必填"articles[].uuid" 值必須是唯一的 version 4 UUID 字串⁶"articles[].uuid" 值永遠不得變更"articles[].slug" 鍵為必填"articles[].slug" 值必須是長度 <= 255 的 kebab-case 字串²"articles[].slug" 值必須有對應的非空 <slug>/content.md 檔案"articles[].slug" 值必須有對應的非空 <slug>/snippet.md 檔案"articles[].title" 鍵為必填"articles[].title" 值必須是長度 <= 255 的 Title Case 字串³"articles[].blurb" 鍵為必填"articles[].blurb" 值必須是長度 <= 350 的非空白字串¹"articles[].authors" 鍵為必填"articles[].authors" 值必須是非空陣列"articles[].authors" 值必須是非空白字串¹"articles[].authors" 值不得有重複"articles[].authors" 值以不分大小寫的方式處理"articles[].contributors" 鍵為選填"articles[].contributors" 值必須是陣列"articles[].contributors" 值必須是非空白字串¹"articles[].contributors" 值不得有重複"articles[].contributors" 值以不分大小寫的方式處理"articles[].authors" 或 "articles[].contributors" 其中一個陣列中(不得重疊).articles/config.json 檔中存在對應的 "articles[].slug" 項目,則此檔案為必填.articles/config.json 檔中存在對應的 "articles[].slug" 項目,則此檔案為必填"[].url" 屬性為必填"[].url" 值必須是 URL"[].description" 屬性為必填"[].description" 值必須是非空白字串¹"[].icon_url" 屬性為選填"[].icon_url" 值必須是 URL"blurb" 鍵為必填"blurb" 值必須是長度 <= 350 的非空白字串¹"authors" 鍵為必填"authors" 值必須是陣列"authors" 值必須是非空白字串¹"authors" 值不得有重複"authors" 值以不分大小寫的方式處理"contributors" 鍵為選填"contributors" 值必須是陣列"contributors" 值必須是非空白字串¹"contributors" 值不得有重複"contributors" 值以不分大小寫的方式處理"authors" 或 "contributors" 其中一個陣列中(不得重疊)非空白字串:至少包含一個非空白字元的字串。
kebab-case 字串:只包含 [a-z0-9] 範圍內字元的字串,可選擇以連字號分隔(例如 "two-fer")。它必須符合以下正規表達式:^[a-z0-9]+(-[a-z0-9]+)*$
Title Case 字串:遵循以下 The Chicago Manual of Style 準則的非空白字串:
Sentence Case 字串:遵循以下準則的非空白字串:
有效的 files 模式:指定練習中所用檔案位置的非空白字串¹,該位置相對於練習的目錄。模式可以使用以下其中一個佔位符:
%{kebab_slug}:kebab-case 形式的練習 slug(例如 bit-manipulation)%{snake_slug}:snake_case 形式的練習 slug(例如 bit_manipulation)%{camel_slug}:camelCase 形式的練習 slug(例如 bitManipulation)%{pascal_slug}:PascalCase 形式的練習 slug(例如 BitManipulation)唯一的 version 4 UUID 字串:符合下列所有條件的字串:
config.json 檔中只出現一次config.json 檔中canonical-data.json 檔中^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
例如,以下 UUID 的形式是正確的:
d334ffe3-657e-4725-a950-290b284b6d9f
你可以執行 configlet uuid 來產生合適的 UUID。
有效的 analyzer tag:格式為 <category>:<thing> 的非空白字串¹。
<category> 值必須是下列其中之一:
paradigm(例如 paradigm:functional)technique(例如 technique:recursion)construct(例如 construct:bitwise-and)uses(例如 uses:DateTime.add_seconds)<thing> 值必須是非空白字串¹。