tinylint
v0.2.0
Published
A tiny browser-first TypeScript-native linter.
Maintainers
Readme
TinyLint
TinyLint is a tiny (~8KB compressed) browser-first TypeScript-native linter. It runs directly on top of the typescript API, avoids parser duplication, and supports three integration modes:
- Direct
ts.Programlinting - Persistent
programProviderlinting - Managed in-memory
LanguageServicelinting - Monaco-friendly managed worker integration via
tinylint/monaco
Install
npm install tinylint typescriptTinyLint ships the lint engine and rule API. You define the rules that fit your project. There are no built-in rules.
Quick Start
import ts from "typescript";
import { createLinter, defineConfig, defineRule, DiagnosticSeverity, LintPhase, lintWithProgram, RuleMeta, RuleScope } from "tinylint";
// Define rules
const noDebuggerRule = defineRule({
meta: new RuleMeta("no-debugger", {
defaultSeverity: DiagnosticSeverity.Error,
requiresTypeInfo: false,
scope: RuleScope.File,
}),
nodeKinds: [ts.SyntaxKind.DebuggerStatement],
create(context) {
const ts = context.typescript;
return {
enter(node) {
if (ts.isDebuggerStatement(node) === false) {
return;
}
context.report(node, "Unexpected debugger statement.");
},
};
},
});
// Create config containing all the rules
const config = defineConfig({
rules: {
"no-debugger": noDebuggerRule,
},
});
// Example 1: lint against an existing ts.Program.
const program: ts.Program = createProgram();
const oneShotResult = lintWithProgram({
config,
currentFile: "/main.ts",
phase: LintPhase.Debounced,
program,
typescript: ts
});
// Example 2: lint in-memory source code with the managed linter.
const managed = createLinter({
config,
files: {
"/main.ts": "debugger;\n",
},
typescript: ts
});
const managedResult = managed.lint({ currentFile: "/main.ts", phase: LintPhase.Debounced });
// Example 3: replace the managed project contents and lint again.
managed.resetProject?.({
files: {
"/other.ts": "debugger;\n"
}
});
const resetResult = managed.lint({ currentFile: "/other.ts", phase: LintPhase.Debounced });Public API
defineRule(rule)adds a custom rule.defineConfig(config)creates a plain-object config.lintWithProgram(...)runs a one-shot pass against a caller-providedts.Program.createLinter(...)creates either a provider-backed or managed persistent linter.resetProject(...)replaces the managed linter's current project and clears its caches.
Monaco Integration
TinyLint does not depend on Monaco Editor, but it exposes a worker-friendly helper at tinylint/monaco.
import ts from "typescript";
import { defineConfig, defineRule, DiagnosticSeverity, LintPhase, RuleMeta, RuleScope } from "tinylint";
import { createMonacoProjectLinter } from "tinylint/monaco";
const linter = createMonacoProjectLinter({
compilerOptions: {
module: ts.ModuleKind.ESNext,
noLib: true,
strict: true,
target: ts.ScriptTarget.ES2020
},
config,
snapshots: [
{
languageId: "typescript",
uri: "file:///main.ts",
value: "debugger;\n",
},
],
typescript: ts
});
const result = linter.lintModel("file:///main.ts", LintPhase.Debounced);
// result.files[0].markers can be passed directly to monaco.editor.setModelMarkers(...)
linter.dispose();