Skip to content

Repository files navigation

OptiPrune analyzer animation

npm versionnpm versionTestsLicenseNode.js

@optiprune/core

@optiprune/core is the headless analysis engine behind OptiPrune. It analyzes TypeScript and JavaScript workspaces and returns structured reports that can be used from CI jobs, editor integrations, dashboards, custom developer tools, and the OptiPrune CLI.

The package does not require the CLI. Import the analysis functions, reporters, cache helpers, fix helpers, and TypeScript contracts directly from the package exports.

What Core provides

AreaCore capability
ReachabilityEntry discovery, module graphs, exports, members, dependency edges, strongly connected components, and cycles.
Source parsingTypeScript, TSX, JavaScript, JSX, and Vue-oriented source handling with parsed, recovered, and fallback parse statuses.
Dynamic pathsLiteral and patterned dynamic-import candidates, unknown dynamic boundaries, unresolved imports, and isolated verification results.
Logic findingsConstant conditions, contradictory guards, unreachable statements, schema-impossible guards, and unreachable dynamic paths.
Project contextPackage manifests, scripts, dependency/devDependency usage, package exports, bins, monorepos, workspaces, and external contracts.
ConfigurationJSON, JSONC, TypeScript, JavaScript, MJS, and package-field configuration through the Core loader.
FixesConfidence-aware file, export/member, dependency, development-dependency, and condition fixes with dry-run and force controls.
OutputStructured AnalysisReport, terminal formatting, JSON serialization, and SARIF 2.1 formatting.
PluginsSource-aware adapters for framework, build, test, runtime, package-manager, and workspace conventions.
Language ServerLSP server over stdio that publishes Core findings as editor diagnostics and reuses the Core cache.

Installation

npm install @optiprune/core
# or
pnpm add @optiprune/core
# or
yarn add @optiprune/core

Core currently requires Node.js 21 or newer.

Basic usage

import { analyze, shouldFail } from "@optiprune/core";
const report = await analyze({
rootDir: process.cwd(),
entry: ["src/index.ts"],
extensions: [".ts", ".tsx", ".js", ".jsx", ".vue"],
output: "json",
});
console.log(report.summary);
console.log(report.findings);
if (shouldFail(report, "high")) {
process.exitCode = 1;
}

analyze() returns a Promise<AnalysisReport>. The report includes the project root, discovered entry points, summary counters, findings, module records, exports, dependency edges, and strongly connected components.

Analyzer options

The main AnalyzerOptions surface includes:

OptionPurpose
rootDirProject directory used as the analysis root.
entryExplicit entry files or glob patterns.
extensionsSource extensions to include.
ignoreAdditional ignore patterns.
ignoreDependenciesDependency names to exclude from dependency findings.
externalContractsPublic symbols or contracts that should be treated as externally consumed.
reportUnusedExportsEnable or disable unused-export reporting.
includeConventionalEntriesInclude conventional framework and project entry points.
includeEntryMembersReport unused members in objects exported directly from entry points; disabled by default.
failOnConfidence threshold used by shouldFail.
outputterminal, json, or sarif.
jsonCompatibility switch for JSON output.
verboseInclude additional diagnostic and graph information.
fixBoolean or FixConfig for opt-in automated fixes.
rulesPer-rule error, warning, or off severity configuration.
pluginsPlugin enablement configuration.
layersSMT, isolated execution, and layer-specific options.

The authoritative configuration reference is schema.json. It is also available from the Core repository.

Configuration

The loader supports the following sources:

SourceFormat
optiprune.jsonStandard JSON.
optiprune.jsoncJSON with comments and trailing commas.
optiprune.config.tsTypeScript default export.
optiprune.config.jsJavaScript ESM default export.
optiprune.config.mjsJavaScript ESM default export.
package.json#optiprunePackage configuration field.

Example TypeScript configuration:

import { defineConfig } from "@optiprune/core";
export default defineConfig({
rootDir: ".",
entry: ["src/index.ts"],
ignore: ["**/fixtures/**"],
reportUnusedExports: true,
failOn: "high",
output: "terminal",
rules: {
"unused-export": "warning",
"unreachable-file": "warning",
"constant-condition": "warning",
"unreachable-dynamic-path": "warning",
},
});

CLI-provided values are applied as explicit overrides. The loader merges project configuration with resolved Core defaults and preserves nested layer, rule, and plugin settings.

Findings and confidence

Each Finding includes a rule, severity, confidence, message, file information, and an optional source location or evidence payload. Current rule names include:

RuleMeaning
unused-exportAn exported symbol is not reachable from the configured roots.
unused-memberAn exported or contracted member is not used.
unreachable-fileA source file is not reachable from the project roots.
unreachable-statementA statement cannot be reached under the analyzed control flow.
constant-conditionA condition is determined to be constant.
contradictory-guardA guard is inconsistent with the path constraints.
unreachable-dynamic-pathA dynamic path has no reachable target under the available evidence.
unknown-dynamic-importA dynamic import cannot be resolved with the available information.
unresolved-importAn import could not be resolved.
parse-recoveryParsing recovered from a source-level diagnostic.
missing-dependencyA referenced package is not declared as a dependency.
missing-script-targetA package script points to a missing target.
unused-dependencyA declared runtime dependency is not used.
unused-dev-dependencyA declared development dependency is not used.
non-existent-dependencyA dependency reference does not resolve to an installed or declared package.
no-entry-pointsNo entry point was discovered for the analyzed project.
protected-contractA symbol is protected by an external contract or configured public surface.
schema-impossible-guardA schema or contract makes a guarded path impossible.

Confidence values are high, medium, low, and info. Severity values are error, warning, and info.

Automated fixes

Fixes are opt-in and can be used through analyze() or the exported applyFixes() helper:

import { analyze, applyFixes } from "@optiprune/core";
const report = await analyze({
rootDir: process.cwd(),
fix: {
rules: ["files", "exports", "dependencies"],
confidence: "medium+",
dryRun: true,
},
});
const changed = await applyFixes(report, process.cwd(), {
rules: ["files", "exports", "dependencies"],
confidence: "medium+",
dryRun: true,
});
console.log(`planned changes: ${changed}`);

Supported fix targets are files, exports, dependencies, devDependencies, and conditions. force allows a selected operation to continue when the source edit is otherwise considered unsafe; dryRun reports planned changes without writing them.

Reporters

The reporters package is available through the @optiprune/core/reporters export:

import { formatSarif, formatTerminal } from "@optiprune/core/reporters";
const terminalText = formatTerminal(report, { showCycles: true });
const sarifText = formatSarif(report);

formatTerminal() produces a human-readable report and can include dependency cycles. formatSarif() serializes findings to SARIF for CI and code-scanning tools. For a JSON report, serialize the AnalysisReport directly.

Cache API

Core exposes cache utilities for reusing analysis state and moving caches between environments:

import {
exportCache,
importCache,
loadCache,
saveCache,
} from "@optiprune/core";
const cache = loadCache(process.cwd());
saveCache(process.cwd(), cache);
await exportCache(process.cwd(), "./.optiprune/cache.json");
await importCache(process.cwd(), "./.optiprune/cache.json");

The cache module also exposes getFileHash() and isCacheValid() for integrations that need to inspect cache freshness.

On an unchanged workspace, analyze() first compares the cached analysis key and inexpensive per-file filesystem metadata (size and mtimeMs). If both match, Core returns the persisted AnalysisReport directly without rereading or hashing source files. If metadata is unavailable or differs, Core falls back to SHA-256 content hashes. During an invalidated run, unchanged files reuse their cached module records, changed files are reparsed, and every file receives a persisted findings array, including an empty array for a clean file.

Language Server

@optiprune/core includes a lightweight Language Server Protocol implementation for editor integrations. It communicates over standard input and output, detects the workspace from the LSP rootUri or workspace folders, runs the Core analyzer, and publishes findings as diagnostics. Diagnostics include the OptiPrune rule code, severity, confidence, source location, and message.

Install the package and build it before starting the server:

npm install @optiprune/core
npm run build
npx optiprune-language-server

The equivalent repository development command is:

npm run language-server

The server reacts to document open, change, and save events. It uses the normal Core cache at <workspace>/.optiprune/cache.json, so repeated editor events on an unchanged workspace return the saved report immediately. When a source file changes, Core invalidates the affected cache state, reparses changed files, reuses unchanged module records, and writes the updated report back to the cache.

A minimal VS Code client configuration can start the stdio server through an extension or another LSP client with the following command:

{
"command": "npx",
"args": ["optiprune-language-server"]
}

The current server focuses on diagnostics. Code actions, hover details, go-to-definition, and a dedicated VS Code extension can be added on top of the same Core report and cache APIs.

Plugins

Plugins implement the AnalyzerPlugin contract and can expose a PluginAdapter and lifecycle hooks. Plugins may add entry patterns, mark files or packages as used, interpret project metadata, and participate in file, AST, dependency, or analysis-complete phases.

import type { AnalyzerPlugin } from "@optiprune/core/types";
export const ExamplePlugin: AnalyzerPlugin = {
name: "example-plugin",
version: "1.0.0",
detect: async () => true,
lifecycle: {
onProjectInit: (adapter) => {
adapter.addEntryPatterns(["src/index.ts"]);
},
onFileStart: (fileId, adapter) => {
adapter.markAsUsed(fileId);
},
},
};

The current built-in implementations live in src/plugins. The plugin registry covers framework, build-tool, test, runtime, package-manager, and workspace conventions.

Public exports

Export pathPublic surface
@optiprune/coreanalyze, shouldFail, applyFixes, exportCache, importCache, and the main runtime API.
@optiprune/core/reportersformatTerminal, formatSarif.
@optiprune/core/typesAnalysisReport, AnalyzerOptions, Finding, FixConfig, plugin contracts, configuration types, graph types, parser types, and result types.
@optiprune/core/fs-utilsFilesystem helpers used by integrations that need Core path and file utilities.
optiprune-language-serverStdio Language Server Protocol process for editor diagnostics.

The package also exports defineConfig, CONFIDENCE_RANK, cache types, report types, module/edge types, monorepo types, and plugin lifecycle types.

Development

Install dependencies and run the package checks from the repository root:

npm install
npm run build
npm test# Run the Language Server regression tests only
npm run test language-server

The build uses TypeScript. The test suite uses Vitest without file-level parallelism.

Acknowledgments

OptiPrune is inspired by and stands on the shoulders of:

  • Knip — for pioneering deep workspace reachability and dead-code analysis in the JavaScript ecosystem.

Links

ResourceLink
Core repositorygithub.com/optiprune/core
CLI repositorygithub.com/optiprune/cli
Core packagenpmjs.com/package/@optiprune/core
Documentation siteopti.drml.int.yt
LicenseMIT

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages