<!-- title({ prefix: "🔬 " }) -->

# 🔬 @kitschpatrol/shared-config

<!-- /title -->

[![NPM Package @kitschpatrol/shared-config](https://img.shields.io/npm/v/@kitschpatrol/shared-config.svg)](https://npmjs.com/package/@kitschpatrol/shared-config)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![CI](https://github.com/kitschpatrol/shared-config/actions/workflows/ci.yml/badge.svg)](https://github.com/kitschpatrol/shared-config/actions/workflows/ci.yml)

<!-- description -->

**A collection of shared configurations, linters, and formatting tools for TypeScript projects. All managed as a single dependency, and invoked via a single CLI command.**

<!-- /description -->

<!-- table-of-contents({ depth: 2 }) -->

## Table of contents

- [Overview](#overview)
- [Getting started](#getting-started)
- [Usage](#usage)
- [Implementation notes](#implementation-notes)
- [Development notes](#development-notes)
- [Background](#background)
- [License](#license)

<!-- /table-of-contents -->

## Overview

This project consolidates most of the configuration and tooling shared by my open-source and internal TypeScript-based projects into a single dependency with a single CLI meta-command to lint and fix issues.

By installing `@kitschpatrol/shared-config` and then running `ksc`, you can run a half-dozen pre-configured code quality and linting tools in one shot. This spares you from cluttering your project's `devDependencies` with packages tangential to the task at hand.

If you don't plan to customize tool configurations, `ksc init` exposes an option to store references to each tool's shared configuration in your `package.json` instead of in files in your project root (at least where permitted by the tool). This can save a bit of file clutter in your project's root directory, at the expense of the immediate discoverability of the tools.

In addition, each tool exports a typed configuration factory function to simplify specifying and extending the default configuration.

The command name `ksc` is just an initialism for "Kitschpatrol Shared Configuration".

### Tools

It takes care of dependencies, configuration, invocation, and reporting for the following tools:

- [ESLint](https://eslint.org) (including Svelte, Astro, React, and TypeScript support — including type-checked rules)
- [Prettier](https://prettier.io) (including a bunch of extra plugins)
- [Stylelint](https://stylelint.io)
- [TypeScript](https://www.typescriptlang.org/) (including a shared TSConfig)
- [CSpell](https://cspell.org) (bundled with a number of custom dictionaries, and a custom unused-word detector)
- [Case Police](https://github.com/antfu/case-police)
- [Knip](https://knip.dev/)
- [VS Code](https://code.visualstudio.com) (extension recommendations and extension settings)
- [Mdat](https://github.com/kitschpatrol/mdat) (my markdown templating and expansion tool)
- [remarklint](https://github.com/remarkjs/remark-lint)
- Basic repo boilerplate (`.gitignore`, GitHub actions, etc.)

### Packages

This particular readme is for the [`@kitschpatrol/shared-config`](https://www.npmjs.com/package/@kitschpatrol/shared-config) package, which depends on a number of tool-specific packages included in the [`kitschpatrol/shared-config`](https://github.com/kitschpatrol/shared-config) monorepo on GitHub, each of which is documented in additional detail in its respective readme.

#### Primary package

- [`@kitschpatrol/shared-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/shared-config/readme.md) (`ksc` command)

#### Sub-packages

- [`@kitschpatrol/cspell-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/cspell-config/readme.md) (`ksc-cspell` command)
- [`@kitschpatrol/eslint-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/eslint-config/readme.md) (`ksc-eslint` command)
- [`@kitschpatrol/knip-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/knip-config/readme.md) (`ksc-knip` command)
- [`@kitschpatrol/mdat-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/mdat-config/readme.md) (`ksc-mdat` command)
- [`@kitschpatrol/prettier-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/prettier-config/readme.md) (`ksc-prettier` command)
- [`@kitschpatrol/remark-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/remark-config/readme.md) (`ksc-remark` command)
- [`@kitschpatrol/repo-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/repo-config/readme.md) (`ksc-repo` command)
- [`@kitschpatrol/stylelint-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/stylelint-config/readme.md) (`ksc-stylelint` command)
- [`@kitschpatrol/typescript-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/typescript-config/readme.md) (`ksc-typescript` command)

> [!IMPORTANT]
>
> Any of these packages may be installed and run on their own via CLI if desired. However, in general, the idea is to use `@kitschpatrol/shared-config` to easily run them all simultaneously over a repo with a single command with options to either check or (where possible) fix problems, with output aggregated into a single report.

Running `ksc <command>` calls the same command across the entire collection of sub-packages.

So assuming you've installed `@kitschpatrol/shared-config`...

Running:

```sh
ksc init
```

Is the same as running:

```sh
ksc-repo init
ksc-mdat init
ksc-typescript init
ksc-eslint init
ksc-stylelint init
ksc-cspell init
ksc-knip init
ksc-remark init
ksc-prettier init
```

_(Sub-commands are always executed in the above order.)_

The top-level `ksc` command also takes care of some nuances in terms of _which_ sub-packages implement _which_ commands, and which subcommands take arguments.

## Getting started

### Dependencies

[Node](https://nodejs.org) >=22.22.2 is required, and [pnpm](https://pnpm.io) >=11 is recommended. NPM and yarn might work as well, but I haven't tested them.

### Installation

There are a few different ways to integrate `@kitschpatrol/shared-config` into your project, depending on whether you're starting from scratch or adding it to an existing project.

#### Create new project from a template:

The easiest way to get started is to create a new project from a starter template:

```sh
pnpm create @kitschpatrol/project@latest
```

See the [template repository](https://github.com/kitschpatrol/create-project) for more details.

#### Bootstrap from scratch:

Alternatively, this one-liner will bootstrap a new project and open it in VS Code, but the template approach above is preferred:

```sh
git init && pnpm init && pnpm pkg set type="module" && pnpm --package=@kitschpatrol/repo-config dlx ksc-repo init && pnpm add -D @kitschpatrol/shared-config && pnpm ksc init && pnpm i && code .
```

Note that `ksc init` takes an optional `--location package` flag will put as much configuration in your `package.json` as possible instead of creating discrete config files in your project root for each tool. Putting config in `package.json` can save some clutter, but it makes configs less discoverable and can make it clunkier to extend or customize configurations since you don't have the benefit of type safety and autocomplete.

At any point, you can call `ksc init` again with the `--location package` or `--location file` flag to reinitialize your configuration files in one place or the other and restore the default configurations.

#### Add to an existing project:

This might overwrite certain config files, so commit first:

```sh
pnpm --package=@kitschpatrol/repo-config dlx ksc-repo init && pnpm i && pnpm add -D @kitschpatrol/shared-config && pnpm ksc init
```

#### Step-by-step:

1. Install the requisite basic repository configuration files:

   ```sh
   pnpm --package=@kitschpatrol/repo-config dlx ksc-repo init
   ```

2. Install the package:

   ```sh
   pnpm add -D @kitschpatrol/shared-config
   ```

3. Add default config files for all the tools to your project root:

   ```sh
   pnpm ksc init
   ```

   Or, if you don't plan to customize tool configurations, you might want to put as much config as possible under tool-specific keys in 'package.json':

   ```sh
   pnpm ksc init --location package
   ```

4. Add helper scripts to your `package.json`:

   These work a bit like [npm-run-all](https://github.com/mysticatea/npm-run-all) to invoke all of the bundled tools.

   ```json
   {
     "scripts": {
       "fix": "ksc fix",
       "lint": "ksc lint"
     }
   }
   ```

5. Set up GitHub action credentials (if desired)

   The GitHub actions included in @kitschpatrol/repo-config require permissions to create releases and update your repository metadata. You can add these through the GitHub website under the _Settings → Secrets and variables → Actions_ page under the key `PERSONAL_ACCESS_TOKEN`, or with the [GitHub CLI](https://cli.github.com/) and a credential manager like [1Password CLI](https://developer.1password.com/docs/cli/get-started/):

   ```sh
   gh secret set PERSONAL_ACCESS_TOKEN --app actions --body $(op read 'op://Personal/GitHub Mika/PERSONAL_ACCESS_TOKEN')
   ```

   See the [@kitschpatrol/repo-config readme](/packages/repo-config/readme.md#github-configuration) for more details.

## Usage

Various VS Code plugins for the bundled tools should "just work".

To check / lint your entire project, after configuring the `package.json` as shown above:

```sh
pnpm run lint
```

To run all of the tools in a _potentially destructive_ "fix" capacity:

```sh
pnpm run fix
```

### CLI

<!-- cli-help({ command: 'ksc' }) -->

#### Command: `ksc`

Run aggregated @kitschpatrol/shared-config commands.

This section lists top-level commands for `ksc`.

Usage:

```txt
ksc <command>
```

| Command        | Argument    | Description                                                                                                                                                                                 |
| -------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `init`         |             | Initialize configuration files for the entire suite of @kitschpatrol/shared-config tools. Will use option flags where possible if provided, but some of the invoked tools will ignore them. |
| `lint`         | `[files..]` | Lint your project with multiple tools in one go. Will use file arguments / globs where possible if provided, but some of the invoked tools only operate at the package scope.               |
| `fix`          | `[files..]` | Fix your project with multiple tools in one go. Will use file arguments / globs where possible if provided, but some of the invoked tools only operate at the package scope.                |
| `print-config` | `[file]`    | Print aggregated tool configuration data. Will use file arguments / globs where possible if provided, but some of the invoked tools only operate at the package scope.                      |

| Option              | Description         | Type      |
| ------------------- | ------------------- | --------- |
| `--help`<br>`-h`    | Show help           | `boolean` |
| `--version`<br>`-v` | Show version number | `boolean` |

_See the sections below for more information on each subcommand._

#### Subcommand: `ksc init`

Initialize configuration files for the entire suite of @kitschpatrol/shared-config tools. Will use option flags where possible if provided, but some of the invoked tools will ignore them.

Usage:

```txt
ksc init
```

| Option              | Description                                         | Type                 | Default  |
| ------------------- | --------------------------------------------------- | -------------------- | -------- |
| `--location`        | Where to store the configuration.                   | `"file"` `"package"` | `"file"` |
| `--skip`            | Tool names to skip (with or without "ksc-" prefix). | `array`              |          |
| `--help`<br>`-h`    | Show help                                           | `boolean`            |          |
| `--version`<br>`-v` | Show version number                                 | `boolean`            |          |

#### Subcommand: `ksc lint`

Lint your project with multiple tools in one go. Will use file arguments / globs where possible if provided, but some of the invoked tools only operate at the package scope.

Usage:

```txt
ksc lint [files..]
```

| Positional Argument | Description                    | Type    | Default |
| ------------------- | ------------------------------ | ------- | ------- |
| `files`             | Files or glob pattern to lint. | `array` | `[]`    |

| Option              | Description                                         | Type      |
| ------------------- | --------------------------------------------------- | --------- |
| `--skip`            | Tool names to skip (with or without "ksc-" prefix). | `array`   |
| `--help`<br>`-h`    | Show help                                           | `boolean` |
| `--version`<br>`-v` | Show version number                                 | `boolean` |

#### Subcommand: `ksc fix`

Fix your project with multiple tools in one go. Will use file arguments / globs where possible if provided, but some of the invoked tools only operate at the package scope.

Usage:

```txt
ksc fix [files..]
```

| Positional Argument | Description                   | Type    | Default |
| ------------------- | ----------------------------- | ------- | ------- |
| `files`             | Files or glob pattern to fix. | `array` | `[]`    |

| Option              | Description                                         | Type      |
| ------------------- | --------------------------------------------------- | --------- |
| `--skip`            | Tool names to skip (with or without "ksc-" prefix). | `array`   |
| `--help`<br>`-h`    | Show help                                           | `boolean` |
| `--version`<br>`-v` | Show version number                                 | `boolean` |

#### Subcommand: `ksc print-config`

Print aggregated tool configuration data. Will use file arguments / globs where possible if provided, but some of the invoked tools only operate at the package scope.

Usage:

```txt
ksc print-config [file]
```

| Positional Argument | Description                                      | Type     |
| ------------------- | ------------------------------------------------ | -------- |
| `file`              | File or glob pattern to print configuration for. | `string` |

| Option              | Description                                         | Type      |
| ------------------- | --------------------------------------------------- | --------- |
| `--skip`            | Tool names to skip (with or without "ksc-" prefix). | `array`   |
| `--help`<br>`-h`    | Show help                                           | `boolean` |
| `--version`<br>`-v` | Show version number                                 | `boolean` |

<!-- /cli-help -->

Recall that the `@kitschpatrol/shared-config` package aggregates integration and invocation of the other tool-specific packages in this monorepo. Running a cli command on `ksc` effectively runs the same command against all the tool-specific packages.

### API

The package also exports `fix`, `fixFile` functions that run all shared-config tools in sequence, matching the order of `ksc fix`: Mdat → ESLint → Stylelint → Prettier. Each tool silently skips content it doesn't understand.

The CLI is preferred, but there are occasionally edge cases where it's handy to run a string or a generated file through the shared-config pipeline without leaving TypeScript.

```typescript
import { clearCache, fix, fixFile } from '@kitschpatrol/shared-config'

// Fix a string with all tools (defaults to TypeScript)
const fixed = await fix('let x = 1\nconsole.log(x)\n')

// Fix with a file type hint
const fixedCss = await fix(cssSource, 'css')
const fixedMarkdown = await fix(mdSource, 'md')

// Fix a file in place with all tools
await fixFile('./src/index.ts')
await fixFile('./src/styles.css')

// Clear all cached tool modules
clearCache()
```

Each sub-package with a `fix` command also exports its own `fix`, `fixFile`, and `clearCache` for standalone use. See the individual package readmes for details. (With the exception of, `ksc-repo`, whose `fix` behavior is too file-specific to be generically useful.)

## Implementation notes

### Line endings

This project standardizes on LF (`\n`) line endings across all platforms. This is enforced via Prettier's `endOfLine: 'lf'` setting in [`@kitschpatrol/prettier-config`](https://github.com/kitschpatrol/shared-config/blob/main/packages/prettier-config/readme.md).

It is expected that your git configuration is set to avoid automatic line ending conversion. You can ensure this by adding the following to your global git config:

```sh
git config --global core.autocrlf false
```

Future versions of `@kitschpatrol/shared-config` might enforce this via a `.gitattributes` file.

### Windows compatibility

This project works on Windows, but it's recommend to run commands in a [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) environment or a Bash-compatible terminal like [Git Bash](https://git-scm.com/download/win) to avoid potential issues with package scripts.

### `check` vs `lint`

This project combines a mix of tools that regard their core task variously as "linting" or "checking" code and prose.

Across all the tools, I've chosen to use the term "lint" instead of "check" to refer to the read-only evaluation process.

### Package architecture

Each package has a simple `/src/cli.ts` file which defines the behavior of its eponymous binary. The build step turns these into node "binary" scripts, providing default implementations where feasible.

The monorepo must be kept intact, as the sub-packages depend on scripts in the parent during build.

### Hoisting caveats

The pnpm authors consider module hoisting harmful, and I tend to agree, but certain exceptions are carved out as necessary and accommodated via the `pnpm-workspace.yaml` file included in `@kitschpatrol/repo-config`:

- CSpell, remark, mdat, ESLint, and Prettier all need to be hoisted via the `publicHoistPattern` array in `pnpm-workspace.yaml` in order to resolve correctly in `pnpm exec` scripts, VS Code plugins, and elsewhere.

- Even basic file-only packages like `repo-config` seem to need to be hoisted via for their bin scripts to be accessible via `pnpm exec`

- In earlier version of pnpm, `prettier` and `eslint` packages were hoisted by default, but as of pnpm 10 this is [no longer the case](https://github.com/pnpm/pnpm/releases/tag/v10.0.0).

## Development notes

The repo uses placeholders for the bin script for each tool to avoid circular dependency issues during `pnpm install`.

To tell git to ignore changes to the placeholders, run `pnpm run bin-ignore`.

For local development via `pnpm`, use `file:` dependency protocol instead of `link:`

Something to investigate: An [approach](https://github.com/antfu/eslint-config#vs-code-support-auto-fix) to ignoring style rules in VS Code, and possibly migrate all style handling to ESLint instead of Prettier.

## Background

### Motivation

[`xo`](https://github.com/xojs/xo) is really, really close to what I'm after here, but I wanted a few extra tools and preferred to use "first party" VS Code plugins where possible.

More recently, the [Vite+ project / `vp` cli tool](https://viteplus.dev/) seems most similar to what I'm after in terms of proxying a variety of tools behind a unified CLI interface.

[`create-typescript-app`](https://github.com/JoshuaKGoldberg/create-typescript-app) is also excellent, and probably the best starting point for most people for most new projects. However, it does not take a "single top-level dependency" / "single unified CLI" approach. I ended up developing my own project templates in [kitschpatrol/create-project](https://github.com/kitschpatrol/create-project). (This uses the same underlying [project template system](https://www.create.bingo/) as `create-typescript-app`.)

[`antfu/eslint-config`](https://github.com/antfu/eslint-config) and [`@sxzz/eslint-config`](https://github.com/sxzz/eslint-config) inspired the approach to ESLint integration.

### Adjacent projects

- [@voxpelli/eslint-config](https://github.com/voxpelli/eslint-config)
- [1stG/configs](https://github.com/1stG/configs)
- [antfu/eslint-config](https://github.com/antfu/eslint-config)
- [awesome-eslint](https://github.com/dustinspecker/awesome-eslint)
- [Complete](https://complete-ts.github.io/)
- [create-typescript-app](https://github.com/JoshuaKGoldberg/create-typescript-app)
- [envsa/shared-config](https://www.npmjs.com/package/@envsa/shared-config) (Liam Rella's fork of `@kitschpatrol/shared-config`)
- [eslint-config-current-thing](https://github.com/GildedPleb/eslint-config-current-thing) _(Smart!)_
- [eslint-config-hyperse](https://github.com/hyperse-io/eslint-config-hyperse)
- [lass](https://lass.js.org) (xo etc.)
- [lintroll](https://www.npmjs.com/package/lintroll)
- [megalinter](https://github.com/oxsecurity/megalinter) (Multi-language.)
- [neostandard](https://github.com/neostandard/neostandard)
- [NullVoxPopuli/eslint-configs](https://github.com/NullVoxPopuli/eslint-configs)
- [qlty](https://github.com/qltysh/qlty) (Multi-language.)
- [routine-npm-packages](https://github.com/kachkaev/routine-npm-packages) and [example](https://github.com/kachkaev/website)
- [sheriff](https://www.eslint-config-sheriff.dev)
- [standard](https://standardjs.com)
- [sxzz/eslint-config](https://github.com/sxzz/eslint-config)
- [TanStack Config](https://tanstack.com/config/latest)
- [trunk](https://trunk.io)
- [ts-reset](https://github.com/mattpocock/ts-reset)
- [tsconfig/bases](https://github.com/tsconfig/bases/tree/main)
- [vercel/style-guide](https://github.com/vercel/style-guide)
- [Vite+](https://viteplus.dev/)
- [vscode-file-nesting-config](https://github.com/antfu/vscode-file-nesting-config)
- [xo](https://github.com/xojs/xo)
- [ZumerBox](https://github.com/zumerlab/zumerbox)

<!-- license -->

## License

[MIT](license.txt) © [Eric Mika](https://ericmika.com)

<!-- /license -->
