@snailicid3/config
v0.3.1
Published
Shared linting, formatting, and TypeScript config utilities for the snailicid3 monorepo
Maintainers
Readme
@snailicid3/config 🐌
Shared linting, formatting, documentation, TypeScript, Nx, and API Extractor policy.
Repository
- GitHub:
@snailicid3/config•snailicid3
Author
👤 Gillian Tunney
Recommended package manager: pnpm
@snailicid3/config 🐌
Shared lint, formatting, documentation, TypeScript, Nx, and API Extractor policy for Snailicid3 projects.
Release status: npm currently serves
@snailicid3/[email protected]. The policy ownership and compatibility-wrapper changes in this checkout have not been released yet. Config is last in the four-package release rehearsal because its runtime dependency graph reaches node-utils, workspace, and logger.
Ownership boundary
Config owns reusable policy and the generation of its published JSON configuration artifacts.
Generic JSON-file, path, and glob implementations belong to @snailicid3/node-utils; repository
facts and commands belong to @snailicid3/workspace; terminal output belongs to
@snailicid3/logger. Compatibility re-exports and command wrappers remain where consumers still
depend on the old config surface, but they delegate to the owning package.
Included tooling
- eslint • Flat config with TypeScript, import, jsdoc, and sort rules
- prettier • Shared Prettier options
- markdownlint-cli2 • Markdown linting rules
- commitlint • Conventional commit configuration
- api-extractor • API report and declaration rollup config
- typedoc • TypeDoc config builders for standard, markdown, VitePress, and material-theme docs
- typescript • Base tsconfig presets:
base,library,typecheck,docs - nx • Shared pipeline preset:
namedInputs+targetDefaultsconsumed vianx.json > extends
Published entry points
| Entry | Purpose |
| -------------------------------------------- | ----------------------------------------------- |
| @snailicid3/config | JavaScript configuration builders and utilities |
| @snailicid3/config/prettier | Generated Prettier JSON |
| @snailicid3/config/markdownlint | Generated markdownlint JSON |
| @snailicid3/config/nx-preset.json | Generated Nx preset |
| @snailicid3/config/api-extractor/base.json | Generated API Extractor base |
| @snailicid3/config/tsconfig/* | TypeScript presets |
The package temporarily exposes compatibility bins for snail-sh, workspace hooks, scoped commands,
changesets, setup, uninstall, and patching because [email protected] published those command names. The
wrappers delegate according to package metadata; logger owns snail-sh and workspace owns the
repository-aware commands. New consumers should install @snailicid3/logger and
@snailicid3/workspace directly. The compatibility bins may be removed only in an explicit breaking
release after consumers have migrated.
Installation
pnpm add --save-dev @snailicid3/configExamples
All TypeScript config builders require cwd. Pass import.meta from the config file when the
configuration should resolve paths relative to that file.
ESLint
Basic Config
/* @file eslint.config.ts */
import { EsLint } from '@snailicid3/config'
const config = EsLint.config({ cwd: import.meta })
export default EsLint.defineConfig(config)Overriding Config
This example appends an extra ignore pattern.
/* @file eslint.config.ts */
import { EsLint } from '@snailicid3/config'
const config = EsLint.config({
cwd: import.meta,
ignores: ['packages/**/docs/**/*'],
})
export default EsLint.defineConfig(config)Overriding Rules
This example appends a custom flat-config entry.
/* @file eslint.config.ts */
import { EsLint, type EsLintConfig, expandExtensions, TS_FILE_EXTENSIONS } from '@snailicid3/config'
const overrideExample: EsLintConfig[number] = {
/** Expands a list of file extensions by appending them to a normalized base pattern. */
files: expandExtensions(TS_FILE_EXTENSIONS, '**/src/**/*'),
name: 'Naming: allow ids for parameters',
rules: {
'@typescript-eslint/naming-convention': [
'error',
{
custom: {
match: true,
regex: '^([a-zA-Z][a-zA-Z0-9_]{2,}|id|db|fs|ctx|req|res)$',
},
format: ['camelCase'],
selector: 'parameter',
},
],
},
}
const config = EsLint.config({ cwd: import.meta, overrides: [overrideExample] })
export default EsLint.defineConfig(config)Prettier
Standard Config
/* @file prettier.config.ts */
import { Prettier } from '@snailicid3/config'
export default Prettier.defineConfig(Prettier.config({ cwd: import.meta }))Overriding Config
/* @file prettier.config.ts */
import { Prettier } from '@snailicid3/config'
export default Prettier.defineConfig(
Prettier.config({
cwd: import.meta,
options: {
endOfLine: 'lf',
printWidth: 100,
semi: false,
singleQuote: true,
tabWidth: 4,
trailingComma: 'all',
},
overrides: [
{
files: '**/*.json',
options: {
tabWidth: 4,
},
},
],
}),
)JSON File Config
Use configFile when generating a .prettierrc.json artifact. It keeps plugins as package-name
strings instead of resolved plugin objects.
import { Prettier } from '@snailicid3/config'
const prettierrc = Prettier.configFile({ cwd: import.meta })Markdownlint
/* @file .markdownlint-cli2.mts */
import { Markdownlint } from '@snailicid3/config'
export default Markdownlint.defineConfig(Markdownlint.config({ cwd: import.meta }))Nx
The shared pipeline ships as a generated dist/nx-preset.json, consumed through Nx's extends:
/* @file nx.json */
{
"extends": "@snailicid3/config/nx-preset.json",
"nxCloudId": "…",
"analytics": true,
}Nx merges a preset with a top-level shallow spread, so a consumer nx.json must not redefine
namedInputs or targetDefaults — either would replace the preset's wholesale. Per-package
variance belongs in package.json > nx.targets.
build is deliberately bundler-agnostic (dependsOn: ["build:ts"]), so each package opts into its
own bundler, otherwise it compiles types but never emits dist:
/* @file packages/<name>/package.json */
"nx": { "targets": { "build": { "dependsOn": ["build:ts", "build:tsdown"] } } }Workspace-root targets are namespaced root:* to avoid colliding with package targets. Enable them
from the root package.json — no project.json required:
/* @file package.json */
"nx": {
"targets": {
"root:build": {}, "root:build:ts": {},
"root:clean": {}, "root:clean:ts": {},
"root:lint": {}, "root:fix": {},
"lint:md": {}, "fix:md": {}
}
}The preset is also available programmatically, which is how this repo renders its own committed
nx.json rather than extending an artifact produced by building itself:
import { Nx } from '@snailicid3/config'
const preset = Nx.config({ cwd: process.cwd() }) // { namedInputs, targetDefaults }Lint-Staged
/* @file .lintstagedrc.mts */
import { LintStaged } from '@snailicid3/config'
export default LintStaged.defineConfig(LintStaged.config({ cwd: import.meta }))Commitlint
/* @file commitlint.config.ts */
import { Commitlint } from '@snailicid3/config'
export default Commitlint.defineConfig(
Commitlint.config({
cwd: import.meta,
scopeOptions: {
mergeScopes: ['my-package'],
matchers: {
docs: ['docs/**', '**/*.md'],
actions: null,
},
},
}),
)scopeOptions.matchers maps a commit scope to micromatch glob patterns used by both scope-commit
and scope-affected. A configured key replaces that scope's built-in patterns; set it to null to
disable the built-in mapping. Unspecified defaults remain enabled for actions, notes, and
scripts.
Git workflow environment
Husky delegates lint-staged, commit-message, filename, branch-name, and protected-branch checks to
the workspace-hook Node dispatcher. Workspace environment defaults are defined by its Zod schema:
pnpm run commit:direct -- "message" # sets SKIP_LINT_STAGED=true
SKIP_LINT_STAGED=true git commit -m "chore(root): message"
PROTECTED_BRANCHES=main,master,release pnpm run commit:feat -- "message"SKIP_LINT_STAGED defaults to false. PROTECTED_BRANCHES defaults to the exact branch names
main,master; set it to an empty value to disable branch protection.
TypeDoc
/* @file typedoc.config.ts */
import { Typedoc } from '@snailicid3/config'
export default Typedoc.materialTheme.config({ cwd: import.meta })/* @file typedoc.config.ts */
import { Typedoc } from '@snailicid3/config'
export default Typedoc.markdown.config({ cwd: import.meta })/* @file typedoc.config.ts */
import { Typedoc } from '@snailicid3/config'
export default Typedoc.vitepress.config({ cwd: import.meta })Api-Extractor
Generate or copy the package base config to dist/.api-extractor-base.json, then extend it from the
package API Extractor config.
{
extends: './dist/.api-extractor-base.json',
}The TypeScript builder has the same required cwd contract.
import { ApiExtractor } from '@snailicid3/config'
const config = ApiExtractor.config({ cwd: import.meta })TypeScript
Type Check
Does not emit js files, checks all files in package including .test.ts files.
// @file tsconfig.json
{
extends: '@snailicid3/config/tsconfig.typecheck',
exclude: ['./node_modules'],
files: ['package.json'],
include: [
'./*.ts',
'./*.cts',
'./*.mts',
'./src/**/*.ts',
'./src/**/*.cts',
'./src/**/*.mts',
'./**/*.test.ts',
'./**/*.test.mts',
'./**/*.test.cts',
],
}Library
Creates a folder of declarations and js files in <configDir>/types, suitable for a library
package.
/* @file tsconfig.build.json */
{
extends: '@snailicid3/config/tsconfig.library',
include: ['./src/**/*.ts', './src/**/*.cts', './src/**/*.mts'],
exclude: ['**/*.test.ts', '**/*.test.mts', '**/*.test.cts'],
}Change outDir to <configDir>/dist if not using a bundler. This example overrides the
compilerOptions to create a dist folder of js files.
/* @file tsconfig.build.json */
{
extends: '@snailicid3/config/tsconfig.library',
include: ['./src/**/*.ts', './src/**/*.cts', './src/**/*.mts'],
exclude: ['**/*.test.ts', '**/*.test.mts', '**/*.test.cts'],
compilerOptions: {
outDir: './dist',
},
}Shell Completions
The shell completion install helper can be called through pnpm:
pnpm exec gbt-setupRelease rehearsal
The shared candidate baseline is 68ab0564b2dc0f23b3ce3424beeb12225941c13d. Release config after
node-utils, workspace, and logger are resolvable from the isolated registry. The clean-consumer
checks must verify:
- every exported generated JSON file exists in the packed artifact and matches the source policy
- every TypeScript-config subpath resolves from npm and pnpm installations
- compatibility command wrappers reach the installed logger/workspace binaries
- no
workspace:*dependency is rewritten to an unavailable registry package - the root JavaScript entry loads without relying on monorepo symlinks
The generated files are public API. build-exporter.ts may be replaced only together with another
artifact-generation mechanism; deleting it as cleanup would break published entry points.
Development
pnpm --filter=@snailicid3/config build:nx
pnpm --filter=@snailicid3/config test:nx
pnpm --filter=@snailicid3/config api:report:nx