@dvukovic/style-guide
v0.40.1
Published
My own style guide
Readme
@dvukovic/style-guide
Personal style guide with ESLint, Prettier, Stylelint, CSpell, Knip, jscpd and Lighthouse CI configurations.
Quick Start
npx -y @dvukovic/style-guide@latest initThis interactive CLI will:
- Let you select which tools to configure (ESLint, Prettier, Stylelint, CSpell, Knip, jscpd)
- Ask about your project setup (TypeScript, React/Next.js, testing frameworks)
- Install required dependencies
- Generate configuration files
Manual Installation
yarn add -D @dvukovic/style-guide eslint prettier stylelint cspell knip jscpdESLint
Create eslint.config.js:
import { customDefineConfig, core, typescript } from "@dvukovic/style-guide/eslint"
export default customDefineConfig({
configs: [core(), typescript()],
ignores: ["dist", "build"],
})Available Configs
core()- Essential rules for all JavaScript/TypeScript projectstypescript()- TypeScript parser and rulestypescriptStrict()- Additional strict TypeScript rulesreact()- React framework rulesnext()- Next.js frameworknode()- Node.js specific rulesjest()- Jest testing frameworkvitest()- Vitest testing frameworkplaywright()- Playwright e2e testingmobx()- MobX state managementstorybook()- StorybooktanstackQuery()- TanStack Queryturbo()- TurborepopackageJson()- package.json lintingpackageJsonWorkspace()- package.json linting for monoreposnextIntl()- Locale aware navigation, carrying the barrel import patternsnoBarrels()- Aggregate and current directory barrel imports
Spreadable values for rules whose options ESLint replaces instead of merging:
RESTRICTED_SYNTAX- Enum and wildcard re-export selectorsNO_BARREL_PATTERNS- Barrel import patterns
Customizing Configs
Each factory function accepts a config parameter to extend or override settings:
import { customDefineConfig, core, typescript } from "@dvukovic/style-guide/eslint"
export default customDefineConfig({
configs: [
core(),
typescript({
rules: {
"@typescript-eslint/no-unused-vars": "warn",
},
}),
],
ignores: ["dist"],
})Project Structure
@dvukovic/style-guide/eslint/project-structure wires
eslint-plugin-project-structure.
File composition is shared, the folder structure is assembled from the shared tree plus the folders
this project adds:
import { folderStructure, projectStructure } from "@dvukovic/style-guide/eslint/project-structure"
export default customDefineConfig({
configs: [
projectStructure({
folderStructure: folderStructure({
ignorePatterns: ["src/__generated__/**"],
src: [{ name: "content", children: [{ ruleId: "functionFolder" }] }],
}),
}),
core(),
],
})folderStructure() takes extra children per container: root, src, shared, constants,
modules and ui. It also exports FOLDER_RULES, FILE_RULES, FILE_COMPOSITION,
FUNCTION_SUFFIXES and COMPONENT_SUFFIXES for projects that assemble their own.
In a monorepo, pass projectRoot (for example projectRoot: "apps/web") to projectStructure().
The plugin resolves paths from the folder that holds its node_modules, so without it every path
starts with apps/ and nothing is checked.
Component folders accept X.hooks.ts, X.variants.ts and X.stories.tsx next to the component;
stories are left out of the one-component-per-file rule.
Prettier
Create prettier.config.ts:
import type { Config } from "prettier"
import { core } from "@dvukovic/style-guide/prettier"
const config: Config = {
...core,
}
export default configStylelint
Create stylelint.config.js:
import { core } from "@dvukovic/style-guide/stylelint"
/** @type {import("stylelint").Config} */
const config = {
...core,
}
export default configMantine
postcss-preset-mantine breakpoint variables are not valid media query syntax until postcss expands
them, so the mantine config turns that rule off:
import { core, mantine } from "@dvukovic/style-guide/stylelint"
/** @type {import("stylelint").Config} */
const config = {
...core,
rules: {
...core.rules,
...mantine.rules,
},
}
export default configCSpell
Create cspell.config.js:
import { core } from "@dvukovic/style-guide/cspell"
/** @type {import("cspell").FileSettings} */
const config = {
...core,
ignorePaths: [...core.ignorePaths],
ignoreWords: [],
}
export default configKnip
Create knip.config.ts:
import type { KnipConfig } from "knip"
import { core } from "@dvukovic/style-guide/knip"
const config: KnipConfig = {
...core,
ignore: [],
ignoreDependencies: [...core.ignoreDependencies],
}
export default configjscpd
jscpd reads JSON only, so init writes .jscpd.json from the shared config. The same values are
available as core from @dvukovic/style-guide/jscpd.
Lighthouse CI
Create lighthouserc.cjs:
const { core } = require("@dvukovic/style-guide/lighthouse")
module.exports = core()URLs come from SEO_LH_URLS, and SEO_LH_INP=1 adds the interaction assertion. Both can be passed
as core({ inp, urls }) instead.
CLI
style-guide init # Generate configuration files
style-guide check-dashes # Find em and en dashes in messages and contentcheck-dashes scans src/i18n/messages and src/content, and exits 1 on a hit.
Scripts
Add to your package.json:
{
"scripts": {
"lint": "yarn lint:eslint && yarn lint:prettier && yarn lint:stylelint && yarn lint:cspell && yarn lint:knip && yarn lint:jscpd",
"lint:eslint": "eslint . --cache --concurrency=auto",
"lint:prettier": "prettier --check --cache .",
"lint:stylelint": "stylelint ./**/*.css --cache",
"lint:cspell": "cspell --no-progress --no-summary --unique '**'",
"lint:knip": "knip",
"lint:jscpd": "jscpd",
"lint:fix": "yarn lint:eslint --fix && yarn lint:prettier --write && yarn lint:stylelint --fix"
}
}License
MIT
