@dvukovic/style-guide 0.6.0 → 0.7.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 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 cspellConfig from "@dvukovic/style-guide/cspell"
119
107
 
120
- const config: Config = {
121
- ...core,
108
+ /** @type {import("cspell").FileSettings} */
109
+ const config = {
110
+ ...cspellConfig,
111
+ 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
+ };
@@ -10,3 +10,4 @@ export function promptESLintOptions(): Promise<{
10
10
  }>;
11
11
  export function promptOverwrite(filename: any): Promise<boolean>;
12
12
  export function promptInstall(packageManager: any, packages: any): Promise<boolean>;
13
+ export function promptAddScripts(): Promise<boolean>;
@@ -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.7.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": "./dist/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
  },
@@ -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,9 +6,12 @@ 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 {
14
+ promptAddScripts,
12
15
  promptESLintOptions,
13
16
  promptInstall,
14
17
  promptOverwrite,
@@ -18,6 +21,7 @@ import {
18
21
  const TOOL_PACKAGES = {
19
22
  cspell: "cspell",
20
23
  eslint: "eslint",
24
+ knip: "knip",
21
25
  prettier: "prettier",
22
26
  stylelint: "stylelint",
23
27
  }
@@ -70,14 +74,13 @@ function getMissingPackages(tools) {
70
74
  return missing
71
75
  }
72
76
 
73
- async function installPackages(tools) {
77
+ async function installPackages(tools, packageManager) {
74
78
  const missingPackages = getMissingPackages(tools)
75
79
 
76
80
  if (missingPackages.length === 0) {
77
81
  return
78
82
  }
79
83
 
80
- const packageManager = detectPackageManager()
81
84
  const shouldInstall = await promptInstall(packageManager, missingPackages)
82
85
 
83
86
  if (!shouldInstall) {
@@ -121,6 +124,40 @@ async function writeConfigFile(filename, content) {
121
124
  return true
122
125
  }
123
126
 
127
+ async function updatePackageJsonScripts(tools, packageManager) {
128
+ const scripts = generateScripts(tools, packageManager)
129
+
130
+ if (Object.keys(scripts).length === 0) {
131
+ return
132
+ }
133
+
134
+ try {
135
+ const packageJsonContent = readFileSync("package.json", "utf8")
136
+ const packageJson = JSON.parse(packageJsonContent)
137
+
138
+ packageJson.scripts = {
139
+ ...packageJson.scripts,
140
+ ...scripts,
141
+ }
142
+
143
+ const sortedKeys = Object.keys(packageJson.scripts).sort((left, right) => {
144
+ return left.localeCompare(right)
145
+ })
146
+ const sortedScripts = {}
147
+
148
+ for (const key of sortedKeys) {
149
+ sortedScripts[key] = packageJson.scripts[key]
150
+ }
151
+
152
+ packageJson.scripts = sortedScripts
153
+
154
+ await writeFile("package.json", `${JSON.stringify(packageJson, null, 4)}\n`)
155
+ clack.log.success("Added scripts to package.json")
156
+ } catch {
157
+ clack.log.error("Failed to update package.json scripts")
158
+ }
159
+ }
160
+
124
161
  export async function runInit() {
125
162
  const tools = await promptToolSelection()
126
163
 
@@ -130,7 +167,10 @@ export async function runInit() {
130
167
  eslintOptions = await promptESLintOptions()
131
168
  }
132
169
 
133
- await installPackages(tools)
170
+ const shouldAddScripts = await promptAddScripts()
171
+ const packageManager = detectPackageManager()
172
+
173
+ await installPackages(tools, packageManager)
134
174
 
135
175
  const spinner = clack.spinner()
136
176
 
@@ -162,10 +202,20 @@ export async function runInit() {
162
202
  results.push({ content, filename: "cspell.config.js" })
163
203
  }
164
204
 
205
+ if (tools.includes("knip")) {
206
+ const content = generateKnipConfig()
207
+
208
+ results.push({ content, filename: "knip.config.ts" })
209
+ }
210
+
165
211
  spinner.stop("Configuration files generated")
166
212
 
167
213
  for (const { content, filename } of results) {
168
214
  // eslint-disable-next-line no-await-in-loop -- Sequential writes for user feedback
169
215
  await writeConfigFile(filename, content)
170
216
  }
217
+
218
+ if (shouldAddScripts) {
219
+ await updatePackageJsonScripts(tools, packageManager)
220
+ }
171
221
  }
@@ -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
  })
@@ -180,3 +181,16 @@ export async function promptInstall(packageManager, packages) {
180
181
 
181
182
  return install
182
183
  }
184
+
185
+ export async function promptAddScripts() {
186
+ const addScripts = await clack.confirm({
187
+ initialValue: true,
188
+ message: "Add lint scripts to package.json?",
189
+ })
190
+
191
+ if (clack.isCancel(addScripts)) {
192
+ return false
193
+ }
194
+
195
+ return addScripts
196
+ }
@@ -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"