Expand description
skif as a library: discover a JavaScript or TypeScript monorepo, lint its dependency graph and plan fixes.
The crate is a thin facade over the skif-* crates and behaves like a pure function of its inputs: it never
prints, prompts, exits, reads the environment or the working directory, or writes files. Everything is read
through a FileSystem (OsFs on disk, MemoryFs for tests, BufferFs for unsaved buffers), every path in a
diagnostic or edit is relative to the workspace root, and fixes are returned as data (FixPlan) for the caller
to show, merge or write. The phase timings of skif_core::timing are off unless the host installs a sink there.
use std::path::Path;
use std::sync::Arc;
use skif::{ConfigOverrides, Linter, MemoryFs, Workspace, find_workspace_root, load_config};
let fs = MemoryFs::new()
.with_file("/repo/package.json", r#"{"name": "root", "private": true, "workspaces": ["packages/*"]}"#)
.with_file("/repo/packages/a/package.json", r#"{"name": "a", "version": "1.0.0"}"#);
let root = find_workspace_root(Path::new("/repo/packages/a"), &fs)?;
let config = load_config(&root, &fs, &ConfigOverrides::default())?;
let workspace = Workspace::discover_with_fs(&root, &config, &fs)?;
// The linter reads source files (with `js-analysis`) through the same file system as discovery.
let report = Linter::with_fs(&config, Arc::new(fs.clone()))?.run(&workspace)?;
assert_eq!(report.summary.packages, 2);A host that lints many projects discovers every root below a directory with discover_targets (or
discover_all) and resolves the layered configuration of a single root with resolve_config and a
ConfigCache (the ConfigLoader behind it is public for hosts that load packages themselves):
use std::path::{Path, PathBuf};
use std::sync::Arc;
use skif::{Ceiling, ConfigCache, ConfigOverrides, Linter, MemoryFs, RunOptions, discover_targets, resolve_config};
let fs = MemoryFs::new()
.with_file("/r/skif.toml", "[lint]\nfail-on-warnings = true\n")
.with_file("/r/a/package.json", r#"{"name": "a", "version": "1.0.0"}"#)
.with_file("/r/b/skif.toml", "")
.with_file("/r/b/package.json", r#"{"name": "b", "version": "1.0.0"}"#);
// Layered configuration: every skif.toml from the root up to the ceiling, merged farthest first. The cache is
// shared by the roots of a run so each file is read once; `origins` says which file set which key.
let cache = ConfigCache::new();
let ceiling = Ceiling::VcsOr(PathBuf::from("/r"));
let resolved = resolve_config(Path::new("/r/a"), &ceiling, &fs, &ConfigOverrides::default(), &cache)?;
assert!(resolved.config.lint.fail_on_warnings);
assert_eq!(resolved.sources.len(), 1);
assert!(resolved.origins.last("lint.fail-on-warnings").is_some());
// Multi-root: every workspace and standalone package below a directory, each with its own configuration.
let discovery = discover_targets(Path::new("/r"), &fs, &ConfigOverrides::default())?;
assert!(discovery.failures.is_empty());
assert_eq!(discovery.targets.len(), 2);
for target in &discovery.targets {
let linter = Linter::with_fs(&target.config, Arc::new(fs.clone()))?;
let config_path = target.config_path();
let mut options = RunOptions::default();
if let Some(path) = &config_path {
options = options.with_config_path(path);
}
let report = linter.run_with(&target.workspace, &options)?;
assert!(report.summary.packages >= 1);
}A host that lints repeatedly, such as an editor, passes the same ModuleCache to every
Linter::with_module_cache (feature js-analysis), so only changed files are parsed again, and lints unsaved
buffers through a BufferFs over the disk. Neither changes a report: a cold, a warm and no cache report alike.
A root nested in another cannot see what the outer root imports from it. A host that wants those files counted
lints the enclosing roots first and passes the files their reports list in Report::other_project_files,
made relative to the nested root, to RunOptions::with_extra_entries of the nested root’s run, as skif -r does.
§Features
| Feature | Default | Brings in |
|---|---|---|
manifest | yes | the built-in rules: manifest rules, version and semver group policies and catalog rules |
js-analysis | no | the oxc based JS and TypeScript analysis and the plugin table (Linter adds it to the built-in rules) |
fix | no | apply_fixes and FixPlan |
yaml-fix | no | YAML edits for pnpm-workspace.yaml (implies fix) |
report | no | the renderers (human, JSON, SARIF, GitHub, Code Climate, compact, Markdown, CODEOWNERS), as skif::render |
schema | no | config_schema |
update | no | update: newer published versions from a registry the host supplies, planned as a FixPlan |
§Third-party types in the API
No oxc or rayon type appears in a public signature; reusable thread pools are an Executor. The data model
is the contract for the libraries below, and nothing else from outside skif and the standard library may appear in
a public item (tests/public_api.rs fails when it does). A major release of a listed library is a breaking
release of skif.
| Library | Where it shows |
|---|---|
serde | the Serialize and Deserialize impls of the data model |
serde_json | JsonAction and YamlAction (a fix is data a host may serialize) and the manifest values of a Package |
toml | a rule’s options (RuleProvider::validate_options receives the toml::Table) |
indexmap | the ordered maps of Config |
nodejs_semver | the parsed ranges of a dependency specifier |
The renderers that take serde_json members (render_json_with) are not part of the facade; only the fixed
renderers are re-exported as render.
§The configuration model
Config and every type it holds are re-exported and readable. A host builds one in code with ConfigBuilder
or loads it with load_config and resolve_config. The [workspace] table is WorkspaceConfig, to tell
it from the discovered Workspace.
Modules§
- diagnostic
- The unit of output of every rule.
- render
- The renderers of
Reports: human, JSON, SARIF, GitHub annotations, Code Climate, compact, Markdown and CODEOWNERS groups. - update
skif update: newer published versions for the dependencies the version policies govern (ADR 0058).
Structs§
- Applied
Fix - A fix that is part of the plan.
- Assignment
- One place that set a key.
- Buffer
Fs basewith the text of some files replaced by buffers, as an editor holds them before they are saved.- Cached
Fs - Wraps a
FileSystemand memoizes what a run asks of it repeatedly. - Cached
Module - The module cache of ADR 0066, re-exported for hosts that keep one.
One cached parse result. Its fields are private: a host stores and serializes it without depending on the shape
of
ModuleInfo. - Catalog
[catalog]- Config
- The complete
skif.tomlmodel. - Config
Builder - Builds a
Configprogrammatically, starting fromConfig::default. - Config
Cache - What a run learns about configuration files, shared by every root it loads: which directories hold a file or a
.git, and each file read and parsed once, whichever roots andextendsreach it. - Config
Loader - Resolves the configuration of a root: the chain of files that govern it, merged.
- Config
Overrides - The CLI layer: typed command-line flags applied over a loaded configuration.
- Config
Source - One configuration file applied to a root.
- Custom
Type [[custom-types]]and the places a version can be declared. One[[custom-types]]entry: a field ofpackage.jsonthat holds versions. Name it in adependency-typeslist to select its instances, like a dependency section (syncpack’scustomTypes).- Dependency
Family - One
[[dependency-families]]entry: packages that must share a version. - Dependency
Import - One import of the dependency.
- Dependency
Listing - A manifest that lists the dependency.
- Dependency
Trace - What
JsAnalysis::trace_dependencyreturns. - Dependency
Types - A list of dependency types with
!negation, e.g.["prod", "!peer"]. - Diagnostic
- A single finding reported by a rule.
- Diagnostic
Builder - Fluent builder returned by
diagnostic. - DirEntry
- A directory entry returned by
FileSystem::read_dir. - Discover
Options - Bounds for
scan_candidates. - Discovery
- The outcome of
discover_targets. - Emitter
- Collects the diagnostics of one or more rules and applies policy centrally.
- Enabled
Rules - The effective severity of every enabled rule.
- Executor
- A reusable pool of worker threads that lint runs execute on.
- Executor
Error - The worker threads of an
Executorcould not be started. - Export
Kinds - The kinds of export
ignore-exports-used-in-filecan name, as in knip. - Export
Report - Why one export of a module counts as used or not.
- Export
User - A module that imports an export.
- Fix
- A titled, ordered group of operations that repairs one diagnostic.
- FixPlan
- The file changes selected fixes amount to. Paths are relative to the workspace root; nothing has been written.
- FixSelection
- Which fixes go into a
FixPlan. - Format
Config - The
[format]table of the configuration: whatpackage-json-formatchecks.[format]: what thepackage-json-formatrule andskif formatcheck, with syncpack’s defaults (ADR 0055). - Group
Ref - The state of one dependency instance, from
Linter::instances. The version group that owns an instance. - Ignore
Entry - One
[[ignore]]suppression. An empty list does not narrow that dimension, but at least one list must be set: an entry without any filter is rejected as it would suppress everything. - Importer
- One import of a module.
- Incomplete
Entries - Why the entry files of a package are not all known, so its files and dependencies are not judged unused.
- Incomplete
Package - A package whose entry files are not all known, with every reason.
- Instance
Filter - Narrows the dependency instances the version-group policies see, as syncpack’s command-line filters do: an instance that does not pass is left out before it is assigned to a group, so it is neither reported nor aligned with. Empty selects everything.
- Instance
Status - The state of one dependency instance, from
Linter::instances. One dependency instance and its state. - Js
[js]- JsWorkspace
[js.workspaces."<dir>"]- Lint
[lint]- Lint
Context - Read-only inputs shared by every rule.
- Linter
- Runs the registered rules over a
Workspaceand returns aReport. - Location
- A file, relative to the workspace root, and an optional position within it.
- Memory
Fs - An in-memory file tree for tests and embedders. Directories exist implicitly as parents of files.
- Memory
Module Cache - The module cache of ADR 0066, re-exported for hosts that keep one.
An in-process
ModuleCachefor long-lived hosts (watch mode, the language server, the MCP server). - Module
Key - The module cache of ADR 0066, re-exported for hosts that keep one.
The key of one parse result: a 128-bit
SipHash-1-3of the schema, the crate version, the parser, the parse options, the file name and the text. The directory is not part of it, so identical files share an entry and a moved checkout keeps its cache. - Origins
- The assignments behind every resolved key, oldest first.
- OsFs
- The real file system.
- Override
- One
[[overrides]]entry: settings for the packages and files it selects. - Override
Scope - Where an
Overridewas declared, with its patterns resolved to absolute ones. - Package
- One
package.jsonof the workspace. - Package
Id - Identifies a package within one workspace: its index among all packages, the root first.
- Package
Js [js]keys an override can set for the packages it selects.- Path
Kind - What a path is, with symbolic links followed. A path that does not exist is neither.
- Plugin
Options - Table form of a plugin entry.
- Policy
[policy]- Provider
Error - A provider failed to run.
- ReExport
- A re-export that forwards an export, with the explanation of the forwarded export.
- Related
- A secondary location that explains a diagnostic.
- Report
- The complete result of a lint run.
- Resolved
Config - The configuration of one root with where it came from.
- Root
Failure - A root that could not be loaded.
- Rule
Config - Configuration of one rule: an optional level plus options.
- RuleId
- Stable kebab-case identifier of a rule, e.g.
unused-dependency. - Rule
Meta - Static description of a rule, used for documentation and listings.
- Rules
Provider - Adapts a list of
Rules into aRuleProvider. - RunOptions
- Host-controlled execution settings for
Linter::run_with. - Selector
- A list of name patterns compiled once.
- Semver
Group - One
[[semver-groups]]entry; first match wins. Setrange, orignored = trueto leave the matched dependencies without a range requirement (syncpack’sisIgnoredandisDisabled). - Skipped
Fix - A diagnostic whose fix is not in the plan.
- Span
- Half-open UTF-8 byte range
[start, end)with 1-based line and column numbers. - Summary
- Aggregate counts over a report.
- Target
- One root to lint: a workspace or a standalone package, with the configuration that governs it.
- Text
Edit - Replacement of the half-open byte range
[start, end);start == endinserts. - Unapplied
Config - A
skif.tomlbelow a root that no configuration of that root was built from. - Update
Config - The
[update]table of the configuration (anupdate::Updateis one proposed version).[update]: howskif updatereads the package registry (ADR 0058). Linting never reads it. - Update
Group - One
[[update-groups]]entry; first match wins. Settargetto cap how farskif updatemoves the matched dependencies, orignored = trueto leave them alone (syncpack’supdateGroups). - Usage
Report - The explanation
JsAnalysis::explain_usagereturns. - Version
Group - One
[[version-groups]]entry; first match wins. Selectors accept!negation and$LOCAL. - Workspace
- A discovered workspace.
- Workspace
Config - The
[workspace]table of the configuration (the discoveredWorkspaceis a different type).[workspace] - Workspace
Protocol [workspace-protocol]
Enums§
- Absence
- Why a file is not in the module graph.
- Alternatives
- What to do when a diagnostic has several alternative fixes and
choicesholds no pick for it. - Catalog
Mode [catalog] mode- Category
- Area of the workspace a rule inspects.
- Ceiling
- How far above a root the search for configuration files goes.
- Config
Error - Everything that can go wrong while locating, reading, parsing or validating configuration.
- Custom
Strategy [[custom-types]]and the places a version can be declared. How aCustomTypereads versions from itspath.- Dependency
Type [[custom-types]]and the places a version can be declared. A place where a dependency version is declared, as a configuration token names it.- Entry
Reason - Which rule makes a module an entry file.
- Error
- Anything that can stop discovery, configuration or a lint run.
- File
Change - The net effect of fixes on one file, ready to be written.
- FixError
- Planning the fixes failed as a whole (as opposed to a single fix being skipped).
- FixOp
- A single mutation of the workspace.
- FixPreference
- The version a fix that is one of several alternatives aligns on, which
[policy] preferselects among them. - FixSafety
- How risky applying a fix is.
- Fixability
- Whether, and how safely, a rule can repair its findings.
- Ignore
Exports Used InFile ignore-exports-used-in-file:true, or a table naming the kinds of export it applies to.- Incomplete
Cause - What made the entry files of a package incomplete.
- Insert
Position - Where a new key is placed inside an object.
- Json
Action - Edit applied to a JSON node.
- Level
- The level a rule is configured to:
off,info,warnorerror. - Lint
Error - A lint run failed.
- Op
- What a file did to a key.
- Package
Manager - Package managers that can be forced with
[workspace] package-manager. - PathSeg
- One step of a structured path into a JSON or YAML document.
- Plugin
Config [js.plugins] <name> = true | { ... }- Prefer
- Which version
preferred-semverfavours. - Registry
Error - Two rules share an ID.
- Reset
Key - The inherited lists that
resetcan discard. - Root
Kind - A project root decided by
claim_roots. - Semver
Range - The range type a semver group enforces.
- Severity
- How serious a diagnostic is. Ordered
Info < Warning < Error. - Skip
Reason - Why a diagnostic’s fix is not in the plan.
- Source
Kind - How a configuration file entered the chain of a root.
- Status
Type - The state of one dependency instance, from
Linter::instances. syncpack’s classes of instance states, asstatusTypenames them. - Unapplied
Reason - Why a configuration file below a root was not applied.
- Update
Target - How far
skif updatemay move a version: syncpack’s--targetand an update group’starget. - Verdict
- Whether an export counts as used.
- Version
Policy - The policy of a version group.
- Workspace
Error - Everything that can stop workspace discovery. Discovery never prints and never exits.
- Yaml
Action - Edit applied to a YAML node.
Constants§
- CONFIG_
FILE_ NAMES - Accepted configuration file names, in lookup order.
Traits§
- File
System - Read-only file system access used by discovery.
- Module
Cache - The module cache of ADR 0066, re-exported for hosts that keep one. A store of parse results the host owns and gives the analysis.
- Rule
- A single lint rule. It detects and reports; policy lives in the
Emitter. - Rule
Provider - A source of rules, such as the manifest rules or a host’s own.
Functions§
- apply_
fixes - Plans the fixes
selectionpicks fromreportand returns them as data; the file system is only read. - builtin_
providers - The rule providers compiled into this build, in registration order.
- config_
file - The name of the configuration file in
root(skif.tomlor.skif.toml), relative toroot, when there is one. - config_
schema - The JSON Schema of
skif.toml, pretty printed. - content_
hash - A stable 64-bit hash (FNV-1a) of file contents, for the staleness guard of a
FixPlan: seeFixPlan::base_hash. It is not cryptographic and will not change between releases. - diagnostic
- Builds a
Diagnosticformeta. - discover_
all - Like
discover_targets, failing with the first root that cannot be loaded. - discover_
targets - Finds every workspace root and standalone package at or below
startand loads each as aTarget, sorted by path (seeskif_workspace::claim_rootsfor what counts as a root). - empty_
tsconfig_ projects - With
[js] use-tsconfig-files, the TypeScript configurations, relative to the workspace root, whosefilesandincludeselect nothing inside their package, so that package has no project files and no file of it can be reported unused. Empty without the setting. A host shows these as warnings; the lint itself goes on. - explain_
usage - Explains why the JavaScript analysis counts
file(relative to the workspace root) as used, and withexportwhy that export of it is used or not. - find_
target - Finds the workspace that
startbelongs to and loads it, discovering it once. - find_
workspace_ root - Finds the workspace that
startbelongs to. - join_
key - Appends
keyto the dotted origin pathprefix. - load_
config - Loads the configuration of
rootthroughfsand layersoverridesover it (defaults < files < overrides). - load_
target - Loads the configuration for
rootfrom every config file inrootor a parent up to theceiling, merged (seeresolve_config), and discovers the workspace atroot. - module_
cache_ version - The version a persisted
ModuleCacherecords: a store written under another version is stale as a whole. The module cache of ADR 0066, re-exported for hosts that keep one. The version a persisted cache records,<crate version>+<CACHE_SCHEMA>: a store written under another version is discarded as a whole. - preferred_
alternative - The index of the alternative in
fixesthat carries theFixPreferencepreferselects, when there is one. - relative_
tsconfig [js] tsconfignormalised to a/-separated path relative to the workspace root, orNonewhen it is empty, absolute or leaves the root. Every reader of the setting goes through this, so they agree on which file it names.- resolve_
config - Resolves the configuration of
rootthroughfs: everyskif.tomlor.skif.tomlfromrootup toceiling(stopping after a file withroot = true), with what theyextend, merged farthest first, thenoverrideson top (defaults < files < overrides). Returns the merged configuration with the files it came from and where each key was set. - slash
pathas text with/separators on every platform, for messages, titles and machine-readable output.- trace_
dependency - Traces the dependency
name: the manifests that list it, every import of it with the importing line, and the packages that count it as used (through imports, scripts, tool configuration or its@typestwin). - unapplied_
config_ files - The
skif.tomland.skif.tomlfiles belowtarget.rootthat its configuration was not built from, sorted by path. - unapplied_
config_ files_ all unapplied_config_filesfor every target of a run at once: entryiis the answer fortargets[i], with the other targets as the deeper roots.
Type Aliases§
- Rule
Options - The raw options table of one rule. The rule that owns the options defines and validates their shape.