@dvukovic/style-guide 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,50 +1,61 @@
1
1
  # @dvukovic/style-guide
2
2
 
3
- Personal style guide with ESLint, Prettier, and other code quality tools
3
+ Personal style guide with ESLint, Prettier, Stylelint, and CSpell configurations.
4
4
 
5
- ## Installation
5
+ ## Quick Start
6
6
 
7
7
  ```bash
8
- yarn add -D @dvukovic/style-guide eslint prettier cspell stylelint
8
+ npx @dvukovic/style-guide@latest init
9
9
  ```
10
10
 
11
- ## ESLint Configuration
11
+ This interactive CLI will:
12
12
 
13
- ### Basic Usage
13
+ - Let you select which tools to configure (ESLint, Prettier, Stylelint, CSpell)
14
+ - Ask about your project setup (TypeScript, React/Next.js, testing frameworks)
15
+ - Install required dependencies
16
+ - Generate configuration files
14
17
 
15
- Create an `eslint.config.js` file in your project root:
18
+ ## Manual Installation
19
+
20
+ ```bash
21
+ yarn add -D @dvukovic/style-guide eslint prettier stylelint cspell knip
22
+ ```
23
+
24
+ ## ESLint
25
+
26
+ Create `eslint.config.js`:
16
27
 
17
28
  ```js
18
29
  import { customDefineConfig, core, typescript } from "@dvukovic/style-guide/eslint"
19
30
 
20
- export default customDefineConfig(["dist/**", "build/**"], [core(), typescript()])
31
+ export default customDefineConfig(["dist", "build"], [core(), typescript()])
21
32
  ```
22
33
 
23
34
  ### Available Configs
24
35
 
25
- Each config is a factory function that returns ESLint configuration:
26
-
27
36
  - `core()` - Essential rules for all JavaScript/TypeScript projects
28
- - `node()` - Node.js specific rules
29
- - `react()` - React framework rules
30
37
  - `typescript()` - TypeScript parser and rules
31
38
  - `typescriptStrict()` - Additional strict TypeScript rules
39
+ - `react()` - React framework rules
40
+ - `next()` - Next.js framework
41
+ - `node()` - Node.js specific rules
32
42
  - `jest()` - Jest testing framework
33
43
  - `vitest()` - Vitest testing framework
34
44
  - `playwright()` - Playwright e2e testing
35
45
  - `mobx()` - MobX state management
36
- - `next()` - Next.js framework
37
46
  - `storybook()` - Storybook
47
+ - `packageJson()` - package.json linting
48
+ - `packageJsonWorkspace()` - package.json linting for monorepos
38
49
 
39
50
  ### Customizing Configs
40
51
 
41
- Each factory function accepts a `config` parameter to extend or override settings:
52
+ Each factory function accepts a config parameter to extend or override settings:
42
53
 
43
54
  ```js
44
55
  import { customDefineConfig, core, typescript } from "@dvukovic/style-guide/eslint"
45
56
 
46
57
  export default customDefineConfig(
47
- [],
58
+ ["dist"],
48
59
  [
49
60
  core(),
50
61
  typescript({
@@ -56,105 +67,87 @@ export default customDefineConfig(
56
67
  )
57
68
  ```
58
69
 
59
- ### Scripts
70
+ ## Prettier
60
71
 
61
- Add these scripts to your `package.json`:
72
+ Create `prettier.config.ts`:
62
73
 
63
- ```json
64
- {
65
- "scripts": {
66
- "lint": "yarn lint:eslint && yarn lint:prettier && yarn lint:stylelint && yarn lint:spell",
67
- "lint:eslint": "eslint . --cache --concurrency=auto",
68
- "lint:fix": "yarn lint:eslint --fix && yarn lint:prettier --write && yarn lint:stylelint --fix && yarn lint:spell",
69
- "lint:prettier": "prettier --check --cache .",
70
- "lint:spell": "cspell --no-progress --no-summary --unique '**'",
71
- "lint:stylelint": "stylelint ./**/*.css --cache",
72
- "test": "vitest run"
73
- }
74
+ ```ts
75
+ import type { Config } from "prettier"
76
+
77
+ import { core } from "@dvukovic/style-guide/prettier"
78
+
79
+ const config: Config = {
80
+ ...core,
74
81
  }
82
+
83
+ export default config
75
84
  ```
76
85
 
77
- ### Complete Example
86
+ ## Stylelint
78
87
 
79
- A full-featured project (this is the actual config used by this package):
88
+ Create `stylelint.config.js`:
80
89
 
81
90
  ```js
82
- import {
83
- customDefineConfig,
84
- core,
85
- node,
86
- mobx,
87
- react,
88
- next,
89
- typescript,
90
- typescriptStrict,
91
- jest,
92
- vitest,
93
- playwright,
94
- } from "@dvukovic/style-guide/eslint"
91
+ import stylelintConfig from "@dvukovic/style-guide/stylelint"
95
92
 
96
- export default customDefineConfig(
97
- ["node_modules"],
98
- [
99
- core(),
100
- node(),
101
- mobx(),
102
- react(),
103
- next(),
104
- typescript(),
105
- typescriptStrict(),
106
- jest(),
107
- vitest(),
108
- playwright(),
109
- ],
110
- )
93
+ /** @type {import("stylelint").Config} */
94
+ const config = {
95
+ ...stylelintConfig,
96
+ }
97
+
98
+ export default config
111
99
  ```
112
100
 
113
- ## Prettier Configuration
101
+ ## CSpell
114
102
 
115
- ```js prettier.config.ts
116
- import type { Config } from "prettier"
103
+ Create `cspell.config.js`:
117
104
 
118
- import core from "@dvukovic/style-guide/src/prettier/configs/core.js"
105
+ ```js
106
+ import { core } from "@dvukovic/style-guide/cspell"
119
107
 
120
- const config: Config = {
108
+ /** @type {import("cspell").FileSettings} */
109
+ const config = {
121
110
  ...core,
111
+ ignorePaths: [...core.ignorePaths],
112
+ ignoreWords: [],
122
113
  }
123
114
 
124
115
  export default config
125
116
  ```
126
117
 
127
- ## Stylelint Configuration
118
+ ## Knip
128
119
 
129
- ```js stylelint.config.js
130
- /** @type {import("stylelint").Config} */
131
- module.exports = {
132
- extends: "@dvukovic/style-guide/src/stylelint/configs/core",
133
- allowEmptyInput: true,
120
+ Create `knip.config.ts`:
121
+
122
+ ```ts
123
+ import type { KnipConfig } from "knip"
124
+
125
+ import { core } from "@dvukovic/style-guide/knip"
126
+
127
+ const config: KnipConfig = {
128
+ ...core,
129
+ ignore: [],
130
+ ignoreDependencies: [...core.ignoreDependencies],
134
131
  }
132
+
133
+ export default config
135
134
  ```
136
135
 
137
- ## Cspell Configuration
136
+ ## Scripts
138
137
 
139
- ```cspell.config.js
140
- /** @type {import("cspell").FileSettings} */
141
- module.exports = {
142
- cache: {
143
- cacheLocation: "./node_modules/.cache/cspell",
144
- useCache: true,
145
- },
146
- caseSensitive: false,
147
- ignorePaths: [],
148
- dictionaries: ["shared"],
149
- dictionaryDefinitions: [
150
- {
151
- name: "shared",
152
- path: "./node_modules/@dvukovic/style-guide/src/cspell/base.txt",
153
- },
154
- ],
155
- useGitignore: true,
156
- ignoreWords: [
157
- ],
138
+ Add to your `package.json`:
139
+
140
+ ```json
141
+ {
142
+ "scripts": {
143
+ "lint": "yarn lint:eslint && yarn lint:prettier && yarn lint:stylelint && yarn lint:cspell && yarn lint:knip",
144
+ "lint:eslint": "eslint . --cache --concurrency=auto",
145
+ "lint:prettier": "prettier --check --cache .",
146
+ "lint:stylelint": "stylelint ./**/*.css --cache",
147
+ "lint:cspell": "cspell --no-progress --no-summary --unique '**'",
148
+ "lint:knip": "knip",
149
+ "lint:fix": "yarn lint:eslint --fix && yarn lint:prettier --write && yarn lint:stylelint --fix"
150
+ }
158
151
  }
159
152
  ```
160
153
 
@@ -0,0 +1 @@
1
+ export function generateKnipConfig(): string;
@@ -0,0 +1,9 @@
1
+ export function generateScripts(tools: any, packageManager: any): {
2
+ "lint:eslint": string;
3
+ "lint:prettier": string;
4
+ "lint:stylelint": string;
5
+ "lint:cspell": string;
6
+ "lint:knip": string;
7
+ lint: string;
8
+ "lint:fix": string;
9
+ };
@@ -1 +1 @@
1
- export { default as core, default } from "./configs/core.js";
1
+ export { default as core } from "./configs/core.js";
@@ -1,4 +1,3 @@
1
- export function isExpressionOrIdentifierOrLiteral(node: any): any;
2
1
  export function hasEmptyBody(program: any): boolean;
3
2
  export function hasExpressionBody(program: any): any;
4
3
  export function hasLabeledStatementBody(program: any): boolean;
@@ -0,0 +1,3 @@
1
+ export default config;
2
+ /** @type {import("knip").KnipConfig} */
3
+ declare const config: import("knip").KnipConfig;
@@ -0,0 +1 @@
1
+ export { default as core } from "./configs/core.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dvukovic/style-guide",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "My own style guide",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,6 +21,10 @@
21
21
  "types": "./dist/eslint/index.d.ts",
22
22
  "default": "./src/eslint/index.js"
23
23
  },
24
+ "./knip": {
25
+ "types": "./src/knip/index.d.ts",
26
+ "default": "./src/knip/index.js"
27
+ },
24
28
  "./prettier": {
25
29
  "types": "./dist/prettier/index.d.ts",
26
30
  "default": "./src/prettier/index.js"
@@ -36,16 +40,18 @@
36
40
  "src/cli",
37
41
  "src/cspell",
38
42
  "src/eslint",
43
+ "src/knip",
39
44
  "src/package-json",
40
45
  "src/prettier",
41
46
  "src/stylelint"
42
47
  ],
43
48
  "scripts": {
44
49
  "build": "tsc",
45
- "lint": "yarn lint:eslint && yarn lint:prettier && yarn lint:stylelint && yarn lint:cspell",
50
+ "lint": "yarn lint:eslint && yarn lint:prettier && yarn lint:stylelint && yarn lint:cspell && yarn lint:knip",
46
51
  "lint:cspell": "cspell --no-progress --no-summary --unique '**'",
47
52
  "lint:eslint": "eslint . --cache --concurrency=auto",
48
- "lint:fix": "yarn lint:eslint --fix && yarn lint:prettier --write && yarn lint:stylelint --fix && yarn lint:cspell",
53
+ "lint:fix": "yarn lint:eslint --fix && yarn lint:prettier --write && yarn lint:stylelint --fix && yarn lint:cspell && yarn lint:knip",
54
+ "lint:knip": "knip",
49
55
  "lint:prettier": "prettier --check --cache .",
50
56
  "lint:stylelint": "stylelint ./**/*.css --cache",
51
57
  "release": "release-it",
@@ -54,10 +60,8 @@
54
60
  "dependencies": {
55
61
  "@clack/prompts": "0.10.0",
56
62
  "@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
57
- "@eslint/compat": "2.0.0",
58
63
  "@eslint/config-helpers": "0.4.2",
59
64
  "@next/eslint-plugin-next": "16.1.0",
60
- "@prettier/plugin-xml": "3.4.2",
61
65
  "@rimac-technology/eslint-plugin": "1.5.0",
62
66
  "@stylistic/eslint-plugin": "5.6.1",
63
67
  "@typescript-eslint/parser": "8.50.0",
@@ -96,12 +100,11 @@
96
100
  "typescript-eslint": "8.50.0"
97
101
  },
98
102
  "devDependencies": {
99
- "@dvukovic/style-guide": "0.5.2",
100
103
  "@release-it/conventional-changelog": "10.0.4",
101
- "@types/eslint": "9.6.1",
102
104
  "@types/node": "24.10.4",
103
105
  "cspell": "9.4.0",
104
106
  "eslint": "9.39.2",
107
+ "knip": "5.82.1",
105
108
  "prettier": "3.7.4",
106
109
  "release-it": "19.1.0",
107
110
  "stylelint": "16.26.1",
@@ -111,6 +114,7 @@
111
114
  "peerDependencies": {
112
115
  "cspell": "9",
113
116
  "eslint": "^9",
117
+ "knip": "5",
114
118
  "prettier": "3",
115
119
  "stylelint": "16"
116
120
  },
@@ -121,6 +125,9 @@
121
125
  "eslint": {
122
126
  "optional": true
123
127
  },
128
+ "knip": {
129
+ "optional": true
130
+ },
124
131
  "prettier": {
125
132
  "optional": true
126
133
  },
@@ -1,10 +1,10 @@
1
1
  export function generateCspellConfig() {
2
- return `import cspellConfig from "@dvukovic/style-guide/cspell"
2
+ return `import { core } from "@dvukovic/style-guide/cspell"
3
3
 
4
4
  /** @type {import("cspell").FileSettings} */
5
5
  const config = {
6
- ...cspellConfig,
7
- ignorePaths: [],
6
+ ...core,
7
+ ignorePaths: [...core.ignorePaths],
8
8
  ignoreWords: [],
9
9
  }
10
10
 
@@ -0,0 +1,14 @@
1
+ export function generateKnipConfig() {
2
+ return `import type { KnipConfig } from "knip"
3
+
4
+ import { core } from "@dvukovic/style-guide/knip"
5
+
6
+ const config: KnipConfig = {
7
+ ...core,
8
+ ignore: [],
9
+ ignoreDependencies: [...core.ignoreDependencies],
10
+ }
11
+
12
+ export default config
13
+ `
14
+ }
@@ -1,10 +1,10 @@
1
1
  export function generatePrettierConfig() {
2
2
  return `import type { Config } from "prettier"
3
3
 
4
- import prettierConfig from "@dvukovic/style-guide/prettier"
4
+ import { core } from "@dvukovic/style-guide/prettier"
5
5
 
6
6
  const config: Config = {
7
- ...prettierConfig,
7
+ ...core,
8
8
  }
9
9
 
10
10
  export default config
@@ -0,0 +1,45 @@
1
+ export function generateScripts(tools, packageManager) {
2
+ const runner = packageManager === "npm" ? "npm run" : packageManager
3
+
4
+ const scripts = {}
5
+ const lintParts = []
6
+ const fixParts = []
7
+
8
+ if (tools.includes("eslint")) {
9
+ scripts["lint:eslint"] = "eslint . --cache --concurrency=auto"
10
+ lintParts.push(`${runner} lint:eslint`)
11
+ fixParts.push(`${runner} lint:eslint --fix`)
12
+ }
13
+
14
+ if (tools.includes("prettier")) {
15
+ scripts["lint:prettier"] = "prettier --check --cache ."
16
+ lintParts.push(`${runner} lint:prettier`)
17
+ fixParts.push(`${runner} lint:prettier --write`)
18
+ }
19
+
20
+ if (tools.includes("stylelint")) {
21
+ scripts["lint:stylelint"] = "stylelint ./**/*.css --cache"
22
+ lintParts.push(`${runner} lint:stylelint`)
23
+ fixParts.push(`${runner} lint:stylelint --fix`)
24
+ }
25
+
26
+ if (tools.includes("cspell")) {
27
+ scripts["lint:cspell"] = "cspell --no-progress --no-summary --unique '**'"
28
+ lintParts.push(`${runner} lint:cspell`)
29
+ }
30
+
31
+ if (tools.includes("knip")) {
32
+ scripts["lint:knip"] = "knip"
33
+ lintParts.push(`${runner} lint:knip`)
34
+ }
35
+
36
+ if (lintParts.length > 0) {
37
+ scripts.lint = lintParts.join(" && ")
38
+ }
39
+
40
+ if (fixParts.length > 0) {
41
+ scripts["lint:fix"] = fixParts.join(" && ")
42
+ }
43
+
44
+ return scripts
45
+ }
package/src/cli/init.js CHANGED
@@ -6,7 +6,9 @@ import * as clack from "@clack/prompts"
6
6
 
7
7
  import { generateCspellConfig } from "./generators/cspell.js"
8
8
  import { generateESLintConfig } from "./generators/eslint.js"
9
+ import { generateKnipConfig } from "./generators/knip.js"
9
10
  import { generatePrettierConfig } from "./generators/prettier.js"
11
+ import { generateScripts } from "./generators/scripts.js"
10
12
  import { generateStylelintConfig } from "./generators/stylelint.js"
11
13
  import {
12
14
  promptESLintOptions,
@@ -18,6 +20,7 @@ import {
18
20
  const TOOL_PACKAGES = {
19
21
  cspell: "cspell",
20
22
  eslint: "eslint",
23
+ knip: "knip",
21
24
  prettier: "prettier",
22
25
  stylelint: "stylelint",
23
26
  }
@@ -70,14 +73,13 @@ function getMissingPackages(tools) {
70
73
  return missing
71
74
  }
72
75
 
73
- async function installPackages(tools) {
76
+ async function installPackages(tools, packageManager) {
74
77
  const missingPackages = getMissingPackages(tools)
75
78
 
76
79
  if (missingPackages.length === 0) {
77
80
  return
78
81
  }
79
82
 
80
- const packageManager = detectPackageManager()
81
83
  const shouldInstall = await promptInstall(packageManager, missingPackages)
82
84
 
83
85
  if (!shouldInstall) {
@@ -111,14 +113,46 @@ async function writeConfigFile(filename, content) {
111
113
  if (!overwrite) {
112
114
  clack.log.info(`Skipped ${filename}`)
113
115
 
114
- return false
116
+ return
115
117
  }
116
118
  }
117
119
 
118
120
  await writeFile(filename, content)
119
121
  clack.log.success(`Created ${filename}`)
122
+ }
123
+
124
+ async function updatePackageJsonScripts(tools, packageManager) {
125
+ const scripts = generateScripts(tools, packageManager)
126
+
127
+ if (Object.keys(scripts).length === 0) {
128
+ return
129
+ }
130
+
131
+ try {
132
+ const packageJsonContent = readFileSync("package.json", "utf8")
133
+ const packageJson = JSON.parse(packageJsonContent)
134
+
135
+ packageJson.scripts = {
136
+ ...packageJson.scripts,
137
+ ...scripts,
138
+ }
139
+
140
+ const sortedKeys = Object.keys(packageJson.scripts).sort((left, right) => {
141
+ return left.localeCompare(right)
142
+ })
143
+ const sortedScripts = {}
120
144
 
121
- return true
145
+ for (const key of sortedKeys) {
146
+ sortedScripts[key] = packageJson.scripts[key]
147
+ }
148
+
149
+ packageJson.scripts = sortedScripts
150
+
151
+ await writeFile("package.json", `${JSON.stringify(packageJson, null, 4)}\n`)
152
+ clack.log.success("Added scripts to package.json")
153
+ } catch {
154
+ clack.log.error("Failed to update package.json scripts")
155
+ }
122
156
  }
123
157
 
124
158
  export async function runInit() {
@@ -130,7 +164,9 @@ export async function runInit() {
130
164
  eslintOptions = await promptESLintOptions()
131
165
  }
132
166
 
133
- await installPackages(tools)
167
+ const packageManager = detectPackageManager()
168
+
169
+ await installPackages(tools, packageManager)
134
170
 
135
171
  const spinner = clack.spinner()
136
172
 
@@ -162,10 +198,18 @@ export async function runInit() {
162
198
  results.push({ content, filename: "cspell.config.js" })
163
199
  }
164
200
 
201
+ if (tools.includes("knip")) {
202
+ const content = generateKnipConfig()
203
+
204
+ results.push({ content, filename: "knip.config.ts" })
205
+ }
206
+
165
207
  spinner.stop("Configuration files generated")
166
208
 
167
209
  for (const { content, filename } of results) {
168
210
  // eslint-disable-next-line no-await-in-loop -- Sequential writes for user feedback
169
211
  await writeConfigFile(filename, content)
170
212
  }
213
+
214
+ await updatePackageJsonScripts(tools, packageManager)
171
215
  }
@@ -16,6 +16,7 @@ export async function promptToolSelection() {
16
16
  { label: "Prettier", value: "prettier" },
17
17
  { label: "Stylelint", value: "stylelint" },
18
18
  { label: "CSpell", value: "cspell" },
19
+ { label: "Knip", value: "knip" },
19
20
  ],
20
21
  required: true,
21
22
  })
@@ -153,7 +154,7 @@ export async function promptESLintOptions() {
153
154
 
154
155
  export async function promptOverwrite(filename) {
155
156
  const overwrite = await clack.confirm({
156
- initialValue: false,
157
+ initialValue: true,
157
158
  message: `${filename} already exists. Overwrite?`,
158
159
  })
159
160
 
@@ -12,6 +12,7 @@ const config = {
12
12
  path: "./node_modules/@dvukovic/style-guide/cspell/base.txt",
13
13
  },
14
14
  ],
15
+ ignorePaths: ["TODO.md", "tsconfig.tsbuildinfo", "CHANGELOG.md"],
15
16
  useGitignore: true,
16
17
  }
17
18
 
@@ -1 +1 @@
1
- export { default as core, default } from "./configs/core.js"
1
+ export { default as core } from "./configs/core.js"
@@ -1,4 +1,4 @@
1
- export function isExpressionOrIdentifierOrLiteral(node) {
1
+ function isExpressionOrIdentifierOrLiteral(node) {
2
2
  if (node.type === "Identifier") {
3
3
  return true
4
4
  }
@@ -0,0 +1,14 @@
1
+ /** @type {import("knip").KnipConfig} */
2
+ const config = {
3
+ ignore: [],
4
+ ignoreDependencies: [
5
+ "cspell",
6
+ "eslint",
7
+ "prettier",
8
+ "stylelint",
9
+ "stylelint-no-unused-selectors",
10
+ "stylelint-order",
11
+ ],
12
+ }
13
+
14
+ export default config
@@ -0,0 +1,3 @@
1
+ import type { KnipConfig } from "knip"
2
+
3
+ export declare const core: KnipConfig
@@ -0,0 +1 @@
1
+ export { default as core } from "./configs/core.js"