@datalackey/tooling-core
v1.3.0
Published
Shared logic and CLI framework for the JavaScript tooling ecosystem in this repository.
Downloads
602
Readme
@datalackey/tooling-core
Shared logic and CLI framework for the JavaScript tooling ecosystem in this repository.
This package is the foundation on which all CLI plugins in this workspace are built —
update-markdown-toc,
nx-graph-to-mermaid, and
update-markdown-uml all depend on it.
It can also be used independently to build your own Node.js CLI tools that follow the same
conventions.
Overview
tooling-core provides:
- CLI framework — argument parsing, help generation, plugin descriptor wiring, and
exit code management via
runCliandPluginDescriptor - File processing — recursive directory traversal, single-file and recursive modes,
deterministic ordering via
walkFilesandlistFilesToProcess - Markdown utilities — marker-based content injection (
injectBetweenMarkers), heading extraction, link extraction, fragment and relative link validation - Repository runner — orchestrates file processing with policy-driven error handling
and output formatting via
RepositoryRunner - Shared types — see below
Shared Types
RunConfig— base configuration passed to every file processor; carries mode, verbosity, debug, exclude list, and run mode (updatevscheck)ProcessingStatus— return value from aFileProcessor; one of"updated","unchanged","needs update", or"skipped"FileProcessor— interface a plugin implements; singleprocess(filePath, config)methodPluginDescriptor— declarative plugin metadata; name, description, options, optionalparseOptions,validate, andafterRunhooksPluginOption— describes a single custom CLI flag declared by a pluginParsedCliResult— result returned byparseStandardCli; carries parsed config, positionals, passthrough flags (those custom to your tool), and help/version booleansRunMode—"update"or"check"RunnerPolicy— interface controlling per-file processing decisions and error handling strategy inRepositoryRunnerRunnerDecision—"abort"or"continue"; returned byRunnerPolicy.handleProcessorErrorRepositoryStats— counts of updated, unchanged, needs update, and skipped files returned byRepositoryRunner.run
Installation
npm install @datalackey/tooling-coreNode.js ≥ 18 required. ESM only.
Usage
The primary entry point for plugin authors is runCli. A minimal plugin looks like this:
import { runCli } from "@datalackey/tooling-core";
import type {
PluginDescriptor,
FileProcessor,
RunConfig,
ProcessingStatus,
} from "@datalackey/tooling-core";
// 1. Extend RunConfig to carry your plugin-specific options.
interface MyPluginConfig extends RunConfig {
skipJs: boolean; // skip processing of Javascript files
}
// 2. Declare your plugin's metadata and custom CLI flags.
const descriptor: PluginDescriptor<MyPluginConfig> = {
name: "my-plugin",
description: "do something with all non-Javascript files ",
options: [
{
flag: "--skip-js",
description: "Skip processing and log Skipped: <file> instead",
},
{
flag: "-s",
description: "Short form of --skip-js",
},
],
parseOptions(standard, passthrough): MyPluginConfig {
const skipJs =
passthrough.get("--skip-js") === true ||
passthrough.get("-s") === true;
return {
...standard,
skipJs: skipJs,
};
},
};
// 3. Implement your file processor.
const processor: FileProcessor<MyPluginConfig> = {
process(filePath: string, config: MyPluginConfig): ProcessingStatus {
if (config.skipJs) {
return "skipped";
}
// ... transform filePath content here ...
return "updated";
},
};
// 4. Wire everything together.
await runCli({ descriptor: descriptor, processor: processor });This gives your plugin standard --check, --recursive, --verbose, --quiet,
--debug, and --help flags with no additional configuration, plus your own
--skip-js / -s flag wired through automatically.
For a complete real-world example see
update-markdown-toc.
Package Structure
flowchart TB
subgraph cli["cli"]
end
subgraph markdown["markdown"]
end
subgraph policy["policy"]
end
subgraph repository["repository"]
end
subgraph util["util"]
end
cli --> markdown
cli --> policy
cli --> repository
cli --> util
markdown --> util
policy --> cli
policy --> markdown
policy --> repository
policy --> util
repository --> cli
repository --> markdown
repository --> policy
repository --> util| Package | Description | |---------|-------------| | cli | TBD | | markdown | TBD | | policy | TBD | | repository | TBD | | util | TBD |
cli
classDiagram
direction TB
class ResolvedTargets {
<<type>>
}
class RunCliOptions {
<<interface>>
+descriptor PluginDescriptor<TConfig>
+processor FileProcessor<TConfig>
+argv? string[]
}
class PluginOption {
<<interface>>
+flag string
+description string
+requiresValue? boolean
+valueName? string
}
class PluginDescriptor {
<<interface>>
+name string
+description string
+options PluginOption[]
+parseOptions(this, standard, passthrough) TConfig
+validate(this, config) void
+afterRun(this, files, config) Promise<void>
}
class ParsedCliResult {
<<interface>>
+config TConfig
+positionals string[]
+passthrough string[]
+help boolean
+version boolean
}
class RunMode {
<<type>>
}markdown
classDiagram
direction TB
class MarkerLocation {
<<interface>>
+startMarker string
+endMarker string
+startLine number
+endLine number
+startIndex number
+endIndex number
}
class LinkRecord {
<<interface>>
+href string
+line number
+kind LinkKind
}
class HeadingRecord {
<<interface>>
+line number
+rawText string
+slug string
}
class FileLineRef {
<<interface>>
+file string
+line number
}
class LinkValidationError {
<<interface>>
+link string
+reason string
}
class LinkValidationWarning {
<<interface>>
+link string
+reason string
}
class LinkValidationResult {
<<interface>>
+errors LinkValidationError[]
+warnings LinkValidationWarning[]
+validatedCount number
+skippedCount number
}
class LinkValidationOptions {
<<interface>>
+validateExternal? boolean
+timeoutMs? number
+concurrency? number
+managedBlockStartMarker? string
+managedBlockEndMarker? string
+verbose? boolean
+onVerbose? (message: string) => void
+onDebug? (message: string) => void
}
class LinkKind {
<<type>>
}
class ExternalLinkStatus {
<<type>>
}
LinkValidationError ..|> FileLineRef
LinkValidationWarning ..|> FileLineRefpolicy
classDiagram
direction TB
class RunnerPolicy {
<<interface>>
+shouldPrint(status) boolean
+shouldPrintSummary() boolean
+handleProcessorError(file, error) RunnerDecision
}
class RunnerDecision {
<<type>>
}repository
classDiagram
direction TB
class RepositoryRunner {
-processor FileProcessor<TConfig>
-config TConfig
-policy RunnerPolicy
+run(files) RepositoryStats
-updateCounters(result, stats) void
-printFileStatus(result, file) void
-getInitCounterState() RepositoryStats
-printSummary(stats) void
}
class RepositoryRunnerOptions {
<<interface>>
+processor FileProcessor<TConfig>
+config TConfig
+policy RunnerPolicy
}
class RepositoryStats {
<<interface>>
+updated number
+unchanged number
+needsUpdate number
+skipped number
}
class FileProcessor {
<<interface>>
+process(filePath, config) ProcessingStatus
}
class RunConfig {
<<interface>>
+runMode RunMode
+mode "single" | "recursive"
+recursivePath? string
+exclude string[]
+verbose boolean
+quiet boolean
+debug boolean
+validateExternalLinks boolean
+linkTimeoutMs number
}
class ProcessingStatus {
<<type>>
}util
classDiagram
direction TB
class WalkOptions {
<<interface>>
+rootDir string
+extensions? string[]
+excludeDirs? string[]
}Tech Stack
For the full workspace tech stack see: TECH-STACK.md
Contributing
See javascript/docs/CONTRIBUTING.md for workspace-level setup, build pipeline, and release workflow.
Packaging, Publishing, and Inter-relationship with Other Plugins
This package is one component of a small ecosystem of JavaScript tooling plugins maintained as individual npm packages in this repository. The versioning and release of these packages is governed by a coordinated release policy, and the packages adhere to common design and architectural principles described here.
