CodeBlanks Logo Icon CodeBlanks

Birleştirme

Tüm parçaları birleştirerek çalışan plugin'i yazarsın.

Tümleşik plugin

Çalışan bir lint plugin dört parçayı birleştirir: iskelet (PluginCreateInfo → LanguageService), config okuma (info.config), diagnostic üretimi (getSemanticDiagnostics) ve code fix (getCodeFixesAtPosition). Her ikisinde de ts.forEachChild ile AST'yı gezersin.

function createPlugin(info: ts.server.PluginCreateInfo): ts.LanguageService {
  const ls = info.languageService;
  const prefix = info.config?.prefix ?? "[lint]";
  return {
    ...ls,
    getSemanticDiagnostics: function(fileName) { /* ... */ },
    getCodeFixesAtPosition: function(fileName) { /* ... */ },
  };
}

Çalışan plugin testi

Plugin'i test etmek için bir mock dil servisi oluşturursun: getSemanticDiagnostics ve getCodeFixesAtPosition boş dizi döndürsün, getProgram().getSourceFile test kaynağını versin. Plugin'i çağırıp diagnostic ve fix sayılarını doğrularsın.

const fakeLs = {
  getSemanticDiagnostics: function() { return []; },
  getCodeFixesAtPosition: function() { return []; },
  getProgram: function() { return { getSourceFile: ... }; },
} as unknown as ts.LanguageService;

Uygulama Tümleşik lint plugin

Tümleşik bir plugin yaz: PluginCreateInfo alır, LanguageService döndürür. info.config?.prefix ile prefix oku (varsayılan "[lint]"). getSemanticDiagnostics'i sar: tür eklemesi olmayan VariableDeclaration için Warning diagnostic üret, mesajı prefix ile başlat. getCodeFixesAtPosition'ı sar: aynı düğümler için fixName: "add-any" fix'i üret, : any ekle. Her ikisinde de ts.forEachChild ile gez. Test: kaynak let x = 1;, prefix "[L]"; diagnostic sayısı ve ilk fix adını sayi fixAdi biçiminde yazdır.

Başlangıç kodu

index.ts
import * as ts from "typescript";

// Tümleşik bir plugin yaz:
// - PluginCreateInfo alır, LanguageService döndürür
// - info'dan prefix ayarını oku (varsayılan "[lint]")
// - Diagnostic metodunu sar: tür eklemesi olmayan VariableDeclaration için diagnostic üret
// - Code fix metodunu sar: aynı düğümler için ": any" ekleme fix'i üret
// - Her ikisinde de forEachChild ile AST'yı gez
// Test: kaynak "let x = 1;", prefix "[L]"; diagnostic sayısı ve fix adını yazdır.

// kodunu buraya yaz

Örnek çözüm

index.ts
import ts from "typescript";

function createPlugin(info: ts.server.PluginCreateInfo): ts.LanguageService {
  const ls = info.languageService;
  const prefix: string = info.config?.prefix ?? "[lint]";
  return {
    ...ls,
    getSemanticDiagnostics: function(fileName: string) {
      const prior = ls.getSemanticDiagnostics(fileName);
      const sf = ls.getProgram().getSourceFile(fileName);
      if (!sf) return prior;
      const extras: ts.Diagnostic[] = [];
      function visit(node: ts.Node) {
        if (ts.isVariableDeclaration(node) && !node.type) {
          extras.push({
            file: sf,
            start: node.getStart(sf),
            length: node.getWidth(sf),
            category: ts.DiagnosticCategory.Warning,
            code: 9001,
            messageText: prefix + " Missing type annotation",
          });
        }
        ts.forEachChild(node, visit);
      }
      visit(sf);
      return [...prior, ...extras];
    },
    getCodeFixesAtPosition: function(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: ts.FormatCodeSettings, preferences: ts.UserPreferences) {
      const prior = ls.getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences);
      const sf = ls.getProgram().getSourceFile(fileName);
      if (!sf) return prior;
      const fixes: ts.CodeFixAction[] = [];
      function visit(node: ts.Node) {
        if (ts.isVariableDeclaration(node) && !node.type) {
          fixes.push({
            fixName: "add-any",
            description: "Add type annotation",
            changes: [{
              fileName,
              textChanges: [{ span: { start: node.name.getEnd(), length: 0 }, newText: ": any" }],
            }],
          });
        }
        ts.forEachChild(node, visit);
      }
      visit(sf);
      return [...prior, ...fixes];
    },
  };
}

const sf = ts.createSourceFile("app.ts", "let x = 1;", ts.ScriptTarget.Latest, true);
const fakeLs = {
  getSemanticDiagnostics: function() { return [] as ts.Diagnostic[]; },
  getCodeFixesAtPosition: function() { return [] as ts.CodeFixAction[]; },
  getProgram: function() { return { getSourceFile: function(n: string) { return n === "app.ts" ? sf : undefined; } } as unknown as ts.Program; },
} as unknown as ts.LanguageService;

const info = { languageService: fakeLs, config: { prefix: "[L]" } } as unknown as ts.server.PluginCreateInfo;
const plugin = createPlugin(info);
const diags = plugin.getSemanticDiagnostics("app.ts");
const fixes = plugin.getCodeFixesAtPosition("app.ts", 0, 1, [], {} as ts.FormatCodeSettings, {} as ts.UserPreferences);
console.log(diags.length + " " + fixes[0].fixName);

Beklenen çıktı

1 add-any

Soru ve cevaplar

Dersin sorularını cevaplarıyla birlikte inceleyebilirsin. Kendini denemek istersen dersi interaktif olarak çöz.

1 Çalışan bir lint plugin hangi dörtlüyü birleştirir?

  • PluginCreateInfo + getSemanticDiagnostics + getCodeFixesAtPosition + info.config Doğru cevap
  • getProgram + getSourceFile + getText + writeFile
  • getTypeChecker + writeFile + readFile + getText
  • createSourceFile + JSON.stringify + writeFile + console.log

Açıklama: PluginCreateInfo plugin girdisi, getSemanticDiagnostics diagnostic üretimi, getCodeFixesAtPosition code fix, info.config yapılandırmadır. Bu dörtlü çalışan plugin'i oluşturur.

2 Diagnostic listesi döndür

Soru
      return [...prior, ...____];
Cevap
      return [...prior, ...extras];

Açıklama: Önceki diagnostics (prior) ile eklenen diagnostics (extras) birleştirilir; diags ve errors değişken adları burada yok, fixes code fix için kullanılır.

3 Tümleşik plugin parçaları

Kavram Anlamı
info.config Yapılandırma okunur
getSemanticDiagnostics Diagnostic üretilir
getCodeFixesAtPosition Fix üretilir
ts.forEachChild AST gezilir

Uzman bölümündeki tüm dersler