Skip to main content

create-linter-rule

Create a new TypeSpec linter rule, lint diagnostic, or design guideline checker with a TDD approach, including implementation, tests, documentation, ruleset registration, and changeset. Use this skill when asked to create, add, implement, or write a new linter rule, lint warning, validation rule, or design guideline enforcement for TypeSpec Azure libraries.

معلومات المصدر

المستودع
Azure/typespec-azure
آخر نشاط في المصدر
٤ أغسطس ٢٠٢٦ في ٢٣:٥٢
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢٧
التفرعات
٩١

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
create-linter-rule
description
Create a new TypeSpec linter rule, lint diagnostic, or design guideline checker with a TDD approach, including implementation, tests, documentation, ruleset registration, and changeset. Use this skill when asked to create, add, implement, or write a new linter rule, lint warning, validation rule, or design guideline enforcement for TypeSpec Azure libraries.
allowed-tools
shell
# Create a TypeSpec Linter Rule Follow these steps in order to create a complete, high-quality linter rule. ## Step 1: IDENTIFY Determine the rule metadata before writing any code: - **Package**: `typespec-azure-core` (data-plane rules), `typespec-azure-resource-manager` (ARM rules), or `typespec-client-generator-core` (client SDK generation rules) - **Rule name**: Must follow these naming conventions: - Use kebab-case (lowercase letters, numbers, hyphens) - DO NOT include the package name in the rule ID (the package is already part of the fully-qualified diagnostic code) - Use `no-<thing>` when the rule bans a construct or usage (e.g., `no-nullable`, `no-enum`, `no-format`) - Use `use-<preferred-thing>` when the rule points users to a standard/preferred TypeSpec pattern (e.g., `use-standard-operations`, `use-extensible-enum`) - Keep names short and concise — prefer `no-enum` over `no-enum-type-usage` - **Severity**: All linter rules are warnings; no severity choice is needed - **Target ruleset(s)**: `data-plane`, `resource-manager`, or both - **Description**: One-line explanation of what the rule enforces Choose the package by scope: - Decorators from `typespec-client-generator-core`, or rules only about client SDK generation → `typespec-client-generator-core` - Rules specific to ARM APIs → `typespec-azure-resource-manager` - Rules that apply to both data-plane and ARM, or only data-plane → `typespec-azure-core` ## Step 2: SCAFFOLD Generate all required files using the repo's scaffolding tool: ```bash pnpm create:linter-rule < rule-name > --package < azure-core | azure-resource-manager | client-generator-core > --description "<description>" ``` This creates: - `packages/<pkg>/src/rules/<rule-name>.ts` — rule skeleton - `packages/<pkg>/test/rules/<rule-name>.test.ts` — test skeleton - `packages/<pkg>/src/rules/<rule-name>.md` — docs skeleton (referenced from the rule via `docs: fileRef.fromPackageRoot(...)`) - Updates `packages/<pkg>/src/linter.ts` — registers the rule ## Step 3: WRITE FAILING TESTS FIRST (TDD) Edit `packages/<pkg>/test/rules/<rule-name>.test.ts`: 1. Write tests for **valid code** that should produce no diagnostics (`.toBeValid()`) 2. Write tests for **invalid code** that should produce specific diagnostics (`.toEmitDiagnostics()`) 3. Create **equivalence classes** for the input and write tests covering at least one instance of each class: - Group inputs by how the rule handles them (e.g., for a rule targeting `ModelProperty`): - Simply defined properties - Properties defined using `spread` or `is` - Properties inherited from a base class - Add boundary conditions specific to the rule logic (e.g., for a name-prefix rule): - Properties with the forbidden prefix - Properties with the prefix text in the middle or end of the name - Properties with names shorter than the prefix 4. Always include at least one test verifying that **library types in `Azure.Core` and `Azure.ResourceManager` are not subject to the rule** Test API reference: ```typescript // No diagnostics expected await tester.expect(`model Foo {}`).toBeValid(); // Specific diagnostic expected await tester.expect(`model foo {}`).toEmitDiagnostics([ { code: "@azure-tools/typespec-<pkg>/<rule-name>", severity: "warning", message: "Expected message text", }, ]); // Test code fix (if rule provides one) await tester .expect(`enum Color { red }`) .applyCodeFix("fix-id") .toEqual(`union Color { string, red: "red" }`); ``` Verify tests fail before implementing: ```bash pnpm --filter "@azure-tools/typespec-<pkg>..." build pnpm --filter "@azure-tools/typespec-<pkg>..." test ``` ## Step 4: IMPLEMENT THE RULE Edit `packages/<pkg>/src/rules/<rule-name>.ts`: - Implement visitor logic in the `create(context)` function - Return a `SemanticNodeListener` with the appropriate hooks: - `model` — for model-level checks - `modelProperty` — for property-level checks - `operation` — for operation-level checks - `enum` — for enum checks - `namespace` — for namespace-level checks - `interface` — for interface checks - `union` / `unionVariant` — for union checks - Use `context.reportDiagnostic({ target })` to report violations - Use `paramMessage` from `@typespec/compiler` for interpolated messages - Add `codefixes` array to `reportDiagnostic()` if a fix is possible ## Step 5: VERIFY TESTS PASS ```bash pnpm --filter "@azure-tools/typespec-<pkg>..." build pnpm --filter "@azure-tools/typespec-<pkg>..." test ``` All tests should pass. If not, fix the implementation until they do. ## Step 6: REGISTER IN RULESETS Add the rule to the appropriate ruleset(s): - **Data-plane rules**: Edit `packages/typespec-azure-rulesets/src/rulesets/data-plane.ts` - **ARM rules**: Edit `packages/typespec-azure-rulesets/src/rulesets/resource-manager.ts` TCGC rules generally go in **both** rulesets. Rules in `typespec-azure-core` that apply to both ARM and data-plane specs also go in **both** rulesets. Only rules that are truly ARM-specific go exclusively in `resource-manager.ts`; otherwise, add the rule to both `resource-manager.ts` and `data-plane.ts` as appropriate. Add an entry: `"@azure-tools/typespec-<pkg>/<rule-name>": true,` Every rule MUST be explicitly listed (enabled or disabled). The `validate-rules-defined.test.ts` test will fail otherwise. Verify: ```bash pnpm --filter "@azure-tools/typespec-azure-rulesets..." build pnpm --filter "@azure-tools/typespec-azure-rulesets..." test ``` ## Step 7: WRITE DOCUMENTATION AND REGENERATE DOCS Edit `packages/<pkg>/src/rules/<rule-name>.md`: - Replace all placeholder text - Write a clear description of what the rule checks and why - Provide realistic ❌ Incorrect and ✅ Correct examples using actual TypeSpec patterns - Add a required **`## Impact`** section: a bulleted `**Area:**` line (e.g. `API`, `SDK`, `Emitters`) followed by a short paragraph describing what breaks or degrades when the rule is violated - Add a required **`## Suppression`** section: state whether suppression is acceptable and why, and point to the correct fix. Suppressions use the `#suppress "<rule-id>" "<justification-string>"` **directive** placed on the line above the target — NOT a `// suppress` comment - Add a **`## LintDiff Equivalent`** section when an equivalent LintDiff (azure-openapi-validator) rule exists, deep-linking to the specific rule id in the [OpenAPI authoring automated guidelines](https://github.com/Azure/azure-rest-api-specs/blob/main/documentation/openapi-authoring-automated-guidelines.md). Omit only when no equivalent exists - This file holds only the extended documentation body — the page title, rule id, and short description are generated from the rule definition. `tspd` renders the page at `website/src/content/docs/docs/libraries/<pkg>/rules/<rule-name>.md` - For ARM rules, also update the alphabetized rule table in `website/src/content/docs/docs/howtos/ARM/arm-rules.md` with the documentation URL, LintDiff equivalent, and impact. This table is authored manually and is not updated by `regen-docs`. Then regenerate the library's reference docs (updates the rule listing): ```bash pnpm --filter "@azure-tools/typespec-<pkg>..." build pnpm --filter "@azure-tools/typespec-<pkg>" regen-docs ``` ## Step 8: CREATE CHANGESET ```bash pnpm change add ``` When prompted: - Select change kind: `feature` (new rule) or `fix` (bugfix to existing rule) - Select affected package: `@azure-tools/typespec-<pkg>` - Write a concise description: "Add `<rule-name>` linter rule that <what it does>" ## Step 9: FINAL VALIDATION Run the general pre-PR validation to ensure everything is ready: ```bash pnpm validate:pr ``` This checks: branch is up to date, build passes, tests pass, lint passes, format is clean, spelling is clean, regen-docs is clean, changeset exists, and diff only contains expected files. If any check fails, fix the issue and re-run. Use `pnpm validate:pr --fix` to auto-fix formatting and lint issues. ## Step 10: EXTERNAL INTEGRATION CHECK New linter rules MUST NOT break existing Azure service specs. After pushing your PR: 1. Apply the `int:azure-specs` label to the PR to trigger the External Integration check: `gh pr edit --add-label "int:azure-specs"` 2. If the agent cannot apply the label automatically, tell the user to apply the `int:azure-specs` label manually in the GitHub UI 3. After applying the label, the External Integration workflow will start. Monitor it via `gh run list --workflow=external-integration.yml` 4. This workflow packages your changes and runs TypeSpec validation against all specs in `Azure/azure-rest-api-specs` 5. Wait for the check to pass before requesting review If the check fails, your rule produces diagnostics on existing specs. To resolve: 1. **Apply an API-neutral fix to the spec** (preferred): If the fix doesn't change API behavior, submit a PR to `Azure/azure-rest-api-specs` on the **main** branch. 2. **Suppress the rule**: If the spec cannot be fixed without changing API behavior, add a `#suppress "<rule-id>" "<justification-string>"` directive on the line above the target (NOT a `// suppress` comment). Suppressions always go to the **main** branch. 3. **Fix on typespec-next**: If the fix requires unreleased TypeSpec APIs or behavior, submit to the **typespec-next** branch (uses nightly builds). 4. **Link your spec fix PR**: Always link the spec fix PR in your linter rule PR description. The External Integration workflow: - Builds and packs all typespec-azure packages from your PR - Checks out `Azure/azure-rest-api-specs` (main branch) - Patches in your packaged changes - Runs `tsp-integration azure-specs --stage validate` - Checks for unexpected git changes ## Important Notes - **Import extensions**: Always use `.js` extensions in imports (e.g., `from "./rules/my-rule.js"`) - **Rule URL**: Must match `https://azure.github.io/typespec-azure/docs/libraries/<pkg>/rules/<rule-name>` - **Test coverage**: Aim for ≥ 95% branch coverage of the rule file - **Existing patterns**: Look at similar existing rules in `packages/<pkg>/src/rules/` for reference - **ARM rules can import from azure-core**: `import { ... } from "@azure-tools/typespec-azure-core"`
عرض على GitHub