CodeBlanks Logo Icon CodeBlanks

Configuration

Add plugin configuration options.

Plugin configuration

Plugin configuration is given in the plugins array of tsconfig.json. Each entry carries a name and custom fields. In the plugin code you access these fields via info.config.

// tsconfig.json:
// "plugins": [
//   { "name": "my-plugin", "prefix": "[lint]" }
// ]

Reading configuration

info.config can be undefined; so you read safely with ?. and ??. Providing default values for custom fields makes the plugin robust.

const prefix: string =
  info.config?.prefix ?? "[lint]";

Practice Plugin with configuration

Write a plugin that reads a prefix from info.config?.prefix (default "[lint]"). Wrap getSemanticDiagnostics: for each VariableDeclaration without a type annotation, produce a diagnostic whose message starts with the prefix. Test: source let x = 1;, config.prefix = "[X]"; print the first diagnostic's messageText.

Starter code

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

// Bir plugin yaz: info'dan prefix ayarını oku
// - Yapılandırmadan prefix oku (varsayılan "[lint]")
// - Diagnostic metodunu sar
// - Tür eklemesi olmayan VariableDeclaration için diagnostic üret
// - Mesajı prefix ile başlat
// Test: prefix = "[X]" ile çağır, ilk diagnostic mesajını yazdır.

// kodunu buraya yaz

Sample solution

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];
    },
  };
}

const sf = ts.createSourceFile("app.ts", "let x = 1;", ts.ScriptTarget.Latest, true);
const fakeLs = {
  getSemanticDiagnostics: function() { return [] as ts.Diagnostic[]; },
  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: "[X]" } } as unknown as ts.server.PluginCreateInfo;
const plugin = createPlugin(info);
const diags = plugin.getSemanticDiagnostics("app.ts");
console.log((diags[0] as ts.Diagnostic).messageText);

Expected output

[X] Missing type annotation

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What does info.config carry?

  • Diagnostic list
  • SourceFile
  • Plugin configuration options Correct answer
  • Program object

Explanation: info.config carries the plugin configuration from tsconfig.json; it does not carry a Program, a diagnostic list, or a SourceFile.

2 Read configuration

Question
const prefix: string = info.____?.prefix ?? "[lint]";
Answer
const prefix: string = info.config?.prefix ?? "[lint]";

Explanation: info.config carries the configuration object; options, settings and plugin do not exist on PluginCreateInfo.

3 Config parts

Term Meaning
info.config Carries the configuration object
info.languageService Carries the language service
config.prefix Is a custom option example
?? Provides a default value

All lessons in Expert