@dvukovic/style-guide 0.5.2 → 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 +82 -89
- package/dist/cli/generators/cspell.d.ts +1 -0
- package/dist/cli/generators/eslint.d.ts +1 -0
- package/dist/cli/generators/knip.d.ts +1 -0
- package/dist/cli/generators/prettier.d.ts +1 -0
- package/dist/cli/generators/scripts.d.ts +9 -0
- package/dist/cli/generators/stylelint.d.ts +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/init.d.ts +1 -0
- package/dist/cli/prompts.d.ts +13 -0
- package/dist/cspell/configs/core.d.ts +3 -0
- package/dist/cspell/index.d.ts +1 -0
- package/dist/eslint/rules/no-commented-out-code/no-commented-out-code.utils.d.ts +0 -1
- package/dist/knip/configs/core.d.ts +3 -0
- package/dist/knip/index.d.ts +1 -0
- package/package.json +22 -7
- package/src/cli/generators/cspell.js +13 -0
- package/src/cli/generators/eslint.js +80 -0
- package/src/cli/generators/knip.js +14 -0
- package/src/cli/generators/prettier.js +12 -0
- package/src/cli/generators/scripts.js +45 -0
- package/src/cli/generators/stylelint.js +11 -0
- package/src/cli/index.js +20 -0
- package/src/cli/init.js +221 -0
- package/src/cli/prompts.js +196 -0
- package/src/cspell/configs/core.js +18 -0
- package/src/cspell/index.js +1 -0
- package/src/eslint/rules/no-commented-out-code/no-commented-out-code.utils.js +1 -1
- package/src/knip/configs/core.js +14 -0
- package/src/knip/index.d.ts +3 -0
- package/src/knip/index.js +1 -0
package/README.md
CHANGED
|
@@ -1,50 +1,61 @@
|
|
|
1
1
|
# @dvukovic/style-guide
|
|
2
2
|
|
|
3
|
-
Personal style guide with ESLint, Prettier, and
|
|
3
|
+
Personal style guide with ESLint, Prettier, Stylelint, and CSpell configurations.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Quick Start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
npx @dvukovic/style-guide init
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
This interactive CLI will:
|
|
12
12
|
|
|
13
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
70
|
+
## Prettier
|
|
60
71
|
|
|
61
|
-
|
|
72
|
+
Create `prettier.config.ts`:
|
|
62
73
|
|
|
63
|
-
```
|
|
64
|
-
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
86
|
+
## Stylelint
|
|
78
87
|
|
|
79
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
##
|
|
101
|
+
## CSpell
|
|
114
102
|
|
|
115
|
-
|
|
116
|
-
import type { Config } from "prettier"
|
|
103
|
+
Create `cspell.config.js`:
|
|
117
104
|
|
|
118
|
-
|
|
105
|
+
```js
|
|
106
|
+
import cspellConfig from "@dvukovic/style-guide/cspell"
|
|
119
107
|
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
##
|
|
118
|
+
## Knip
|
|
128
119
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
##
|
|
136
|
+
## Scripts
|
|
138
137
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 generateCspellConfig(): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function generateESLintConfig(options: any): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function generateKnipConfig(): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function generatePrettierConfig(): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function generateStylelintConfig(): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function runInit(): Promise<void>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function promptToolSelection(): Promise<string[]>;
|
|
2
|
+
export function promptESLintOptions(): Promise<{
|
|
3
|
+
extras: ("mobx" | "storybook")[];
|
|
4
|
+
framework: "none" | "react" | "next";
|
|
5
|
+
includeNode: boolean;
|
|
6
|
+
isMonorepo: boolean;
|
|
7
|
+
language: "typescript" | "javascript";
|
|
8
|
+
strictMode: boolean;
|
|
9
|
+
testing: ("jest" | "vitest" | "playwright")[];
|
|
10
|
+
}>;
|
|
11
|
+
export function promptOverwrite(filename: any): Promise<boolean>;
|
|
12
|
+
export function promptInstall(packageManager: any, packages: any): Promise<boolean>;
|
|
13
|
+
export function promptAddScripts(): Promise<boolean>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as core, default } from "./configs/core.js";
|
|
@@ -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.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "My own style guide",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,11 +12,19 @@
|
|
|
12
12
|
},
|
|
13
13
|
"type": "module",
|
|
14
14
|
"exports": {
|
|
15
|
-
"./cspell
|
|
15
|
+
"./cspell": {
|
|
16
|
+
"types": "./dist/cspell/index.d.ts",
|
|
17
|
+
"default": "./src/cspell/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./cspell/base.txt": "./src/cspell/base.txt",
|
|
16
20
|
"./eslint": {
|
|
17
21
|
"types": "./dist/eslint/index.d.ts",
|
|
18
22
|
"default": "./src/eslint/index.js"
|
|
19
23
|
},
|
|
24
|
+
"./knip": {
|
|
25
|
+
"types": "./dist/knip/index.d.ts",
|
|
26
|
+
"default": "./src/knip/index.js"
|
|
27
|
+
},
|
|
20
28
|
"./prettier": {
|
|
21
29
|
"types": "./dist/prettier/index.d.ts",
|
|
22
30
|
"default": "./src/prettier/index.js"
|
|
@@ -26,31 +34,34 @@
|
|
|
26
34
|
"default": "./src/stylelint/index.js"
|
|
27
35
|
}
|
|
28
36
|
},
|
|
37
|
+
"bin": "./src/cli/index.js",
|
|
29
38
|
"files": [
|
|
30
39
|
"dist",
|
|
40
|
+
"src/cli",
|
|
31
41
|
"src/cspell",
|
|
32
42
|
"src/eslint",
|
|
43
|
+
"src/knip",
|
|
33
44
|
"src/package-json",
|
|
34
45
|
"src/prettier",
|
|
35
46
|
"src/stylelint"
|
|
36
47
|
],
|
|
37
48
|
"scripts": {
|
|
38
49
|
"build": "tsc",
|
|
39
|
-
"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",
|
|
40
51
|
"lint:cspell": "cspell --no-progress --no-summary --unique '**'",
|
|
41
52
|
"lint:eslint": "eslint . --cache --concurrency=auto",
|
|
42
|
-
"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",
|
|
43
55
|
"lint:prettier": "prettier --check --cache .",
|
|
44
56
|
"lint:stylelint": "stylelint ./**/*.css --cache",
|
|
45
57
|
"release": "release-it",
|
|
46
58
|
"test": "vitest run"
|
|
47
59
|
},
|
|
48
60
|
"dependencies": {
|
|
61
|
+
"@clack/prompts": "0.10.0",
|
|
49
62
|
"@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
|
|
50
|
-
"@eslint/compat": "2.0.0",
|
|
51
63
|
"@eslint/config-helpers": "0.4.2",
|
|
52
64
|
"@next/eslint-plugin-next": "16.1.0",
|
|
53
|
-
"@prettier/plugin-xml": "3.4.2",
|
|
54
65
|
"@rimac-technology/eslint-plugin": "1.5.0",
|
|
55
66
|
"@stylistic/eslint-plugin": "5.6.1",
|
|
56
67
|
"@typescript-eslint/parser": "8.50.0",
|
|
@@ -90,10 +101,10 @@
|
|
|
90
101
|
},
|
|
91
102
|
"devDependencies": {
|
|
92
103
|
"@release-it/conventional-changelog": "10.0.4",
|
|
93
|
-
"@types/eslint": "9.6.1",
|
|
94
104
|
"@types/node": "24.10.4",
|
|
95
105
|
"cspell": "9.4.0",
|
|
96
106
|
"eslint": "9.39.2",
|
|
107
|
+
"knip": "5.82.1",
|
|
97
108
|
"prettier": "3.7.4",
|
|
98
109
|
"release-it": "19.1.0",
|
|
99
110
|
"stylelint": "16.26.1",
|
|
@@ -103,6 +114,7 @@
|
|
|
103
114
|
"peerDependencies": {
|
|
104
115
|
"cspell": "9",
|
|
105
116
|
"eslint": "^9",
|
|
117
|
+
"knip": "5",
|
|
106
118
|
"prettier": "3",
|
|
107
119
|
"stylelint": "16"
|
|
108
120
|
},
|
|
@@ -113,6 +125,9 @@
|
|
|
113
125
|
"eslint": {
|
|
114
126
|
"optional": true
|
|
115
127
|
},
|
|
128
|
+
"knip": {
|
|
129
|
+
"optional": true
|
|
130
|
+
},
|
|
116
131
|
"prettier": {
|
|
117
132
|
"optional": true
|
|
118
133
|
},
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function generateCspellConfig() {
|
|
2
|
+
return `import cspellConfig from "@dvukovic/style-guide/cspell"
|
|
3
|
+
|
|
4
|
+
/** @type {import("cspell").FileSettings} */
|
|
5
|
+
const config = {
|
|
6
|
+
...cspellConfig,
|
|
7
|
+
ignorePaths: [],
|
|
8
|
+
ignoreWords: [],
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default config
|
|
12
|
+
`
|
|
13
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export function generateESLintConfig(options) {
|
|
2
|
+
const { extras, framework, includeNode, isMonorepo, language, strictMode, testing } = options
|
|
3
|
+
|
|
4
|
+
const imports = ["core", "customDefineConfig"]
|
|
5
|
+
const configs = ["core()"]
|
|
6
|
+
const ignores = ["dist", "node_modules"]
|
|
7
|
+
|
|
8
|
+
if (language === "typescript") {
|
|
9
|
+
imports.push("typescript")
|
|
10
|
+
configs.push("typescript()")
|
|
11
|
+
|
|
12
|
+
if (strictMode) {
|
|
13
|
+
imports.push("typescriptStrict")
|
|
14
|
+
configs.push("typescriptStrict()")
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (framework === "react") {
|
|
19
|
+
imports.push("react")
|
|
20
|
+
configs.push("react()")
|
|
21
|
+
ignores.push("build")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (framework === "next") {
|
|
25
|
+
imports.push("next")
|
|
26
|
+
configs.push("next()")
|
|
27
|
+
ignores.push(".next", "out")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (includeNode) {
|
|
31
|
+
imports.push("node")
|
|
32
|
+
configs.push("node()")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (testing.includes("jest")) {
|
|
36
|
+
imports.push("jest")
|
|
37
|
+
configs.push("jest()")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (testing.includes("vitest")) {
|
|
41
|
+
imports.push("vitest")
|
|
42
|
+
configs.push("vitest()")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (testing.includes("playwright")) {
|
|
46
|
+
imports.push("playwright")
|
|
47
|
+
configs.push("playwright()")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (extras.includes("mobx")) {
|
|
51
|
+
imports.push("mobx")
|
|
52
|
+
configs.push("mobx()")
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (extras.includes("storybook")) {
|
|
56
|
+
imports.push("storybook")
|
|
57
|
+
configs.push("storybook()")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (isMonorepo) {
|
|
61
|
+
imports.push("packageJsonWorkspace")
|
|
62
|
+
configs.push("...packageJsonWorkspace()")
|
|
63
|
+
} else {
|
|
64
|
+
imports.push("packageJson")
|
|
65
|
+
configs.push("packageJson()")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const importStatement = `import {\n ${imports.join(",\n ")},\n} from "@dvukovic/style-guide/eslint"`
|
|
69
|
+
|
|
70
|
+
const ignoresArray = `["${ignores.join('", "')}"]`
|
|
71
|
+
const configsArray = `[\n ${configs.join(",\n ")},\n ]`
|
|
72
|
+
|
|
73
|
+
return `${importStatement}
|
|
74
|
+
|
|
75
|
+
export default customDefineConfig(
|
|
76
|
+
${ignoresArray},
|
|
77
|
+
${configsArray},
|
|
78
|
+
)
|
|
79
|
+
`
|
|
80
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -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/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { intro, outro } from "@clack/prompts"
|
|
4
|
+
|
|
5
|
+
import { runInit } from "./init.js"
|
|
6
|
+
|
|
7
|
+
const command = process.argv[2]
|
|
8
|
+
|
|
9
|
+
if (command === "init") {
|
|
10
|
+
intro("@dvukovic/style-guide")
|
|
11
|
+
await runInit()
|
|
12
|
+
outro("Configuration complete!")
|
|
13
|
+
} else {
|
|
14
|
+
process.stderr.write("Usage: style-guide init\n")
|
|
15
|
+
process.stderr.write("\n")
|
|
16
|
+
process.stderr.write("Commands:\n")
|
|
17
|
+
process.stderr.write(" init Initialize configuration files\n")
|
|
18
|
+
// eslint-disable-next-line n/no-process-exit -- CLI entry point
|
|
19
|
+
process.exit(1)
|
|
20
|
+
}
|
package/src/cli/init.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { execSync } from "node:child_process"
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
3
|
+
import { writeFile } from "node:fs/promises"
|
|
4
|
+
|
|
5
|
+
import * as clack from "@clack/prompts"
|
|
6
|
+
|
|
7
|
+
import { generateCspellConfig } from "./generators/cspell.js"
|
|
8
|
+
import { generateESLintConfig } from "./generators/eslint.js"
|
|
9
|
+
import { generateKnipConfig } from "./generators/knip.js"
|
|
10
|
+
import { generatePrettierConfig } from "./generators/prettier.js"
|
|
11
|
+
import { generateScripts } from "./generators/scripts.js"
|
|
12
|
+
import { generateStylelintConfig } from "./generators/stylelint.js"
|
|
13
|
+
import {
|
|
14
|
+
promptAddScripts,
|
|
15
|
+
promptESLintOptions,
|
|
16
|
+
promptInstall,
|
|
17
|
+
promptOverwrite,
|
|
18
|
+
promptToolSelection,
|
|
19
|
+
} from "./prompts.js"
|
|
20
|
+
|
|
21
|
+
const TOOL_PACKAGES = {
|
|
22
|
+
cspell: "cspell",
|
|
23
|
+
eslint: "eslint",
|
|
24
|
+
knip: "knip",
|
|
25
|
+
prettier: "prettier",
|
|
26
|
+
stylelint: "stylelint",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function detectPackageManager() {
|
|
30
|
+
if (existsSync("pnpm-lock.yaml")) {
|
|
31
|
+
return "pnpm"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (existsSync("yarn.lock")) {
|
|
35
|
+
return "yarn"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (existsSync("bun.lockb")) {
|
|
39
|
+
return "bun"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return "npm"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getInstalledPackages() {
|
|
46
|
+
try {
|
|
47
|
+
const packageJson = JSON.parse(readFileSync("package.json", "utf8"))
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
...packageJson.dependencies,
|
|
51
|
+
...packageJson.devDependencies,
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
return {}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getMissingPackages(tools) {
|
|
59
|
+
const installed = getInstalledPackages()
|
|
60
|
+
const missing = []
|
|
61
|
+
|
|
62
|
+
if (!("@dvukovic/style-guide" in installed)) {
|
|
63
|
+
missing.push("@dvukovic/style-guide")
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const tool of tools) {
|
|
67
|
+
const packageName = TOOL_PACKAGES[tool]
|
|
68
|
+
|
|
69
|
+
if (packageName && !(packageName in installed)) {
|
|
70
|
+
missing.push(packageName)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return missing
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function installPackages(tools, packageManager) {
|
|
78
|
+
const missingPackages = getMissingPackages(tools)
|
|
79
|
+
|
|
80
|
+
if (missingPackages.length === 0) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const shouldInstall = await promptInstall(packageManager, missingPackages)
|
|
85
|
+
|
|
86
|
+
if (!shouldInstall) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const packages = missingPackages.join(" ")
|
|
91
|
+
const installCmd =
|
|
92
|
+
packageManager === "npm"
|
|
93
|
+
? `npm install -D ${packages}`
|
|
94
|
+
: `${packageManager} add -D ${packages}`
|
|
95
|
+
|
|
96
|
+
const spinner = clack.spinner()
|
|
97
|
+
|
|
98
|
+
spinner.start("Installing packages")
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// eslint-disable-next-line sonarjs/os-command -- User-confirmed install command
|
|
102
|
+
execSync(installCmd, { stdio: "pipe" })
|
|
103
|
+
spinner.stop("Packages installed")
|
|
104
|
+
} catch {
|
|
105
|
+
spinner.stop("Failed to install. Please run manually:")
|
|
106
|
+
clack.log.info(` ${installCmd}`)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function writeConfigFile(filename, content) {
|
|
111
|
+
if (existsSync(filename)) {
|
|
112
|
+
const overwrite = await promptOverwrite(filename)
|
|
113
|
+
|
|
114
|
+
if (!overwrite) {
|
|
115
|
+
clack.log.info(`Skipped ${filename}`)
|
|
116
|
+
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await writeFile(filename, content)
|
|
122
|
+
clack.log.success(`Created ${filename}`)
|
|
123
|
+
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
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
|
+
|
|
161
|
+
export async function runInit() {
|
|
162
|
+
const tools = await promptToolSelection()
|
|
163
|
+
|
|
164
|
+
let eslintOptions = null
|
|
165
|
+
|
|
166
|
+
if (tools.includes("eslint")) {
|
|
167
|
+
eslintOptions = await promptESLintOptions()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const shouldAddScripts = await promptAddScripts()
|
|
171
|
+
const packageManager = detectPackageManager()
|
|
172
|
+
|
|
173
|
+
await installPackages(tools, packageManager)
|
|
174
|
+
|
|
175
|
+
const spinner = clack.spinner()
|
|
176
|
+
|
|
177
|
+
spinner.start("Generating configuration files")
|
|
178
|
+
|
|
179
|
+
const results = []
|
|
180
|
+
|
|
181
|
+
if (tools.includes("eslint") && eslintOptions) {
|
|
182
|
+
const content = generateESLintConfig(eslintOptions)
|
|
183
|
+
|
|
184
|
+
results.push({ content, filename: "eslint.config.js" })
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (tools.includes("prettier")) {
|
|
188
|
+
const content = generatePrettierConfig()
|
|
189
|
+
|
|
190
|
+
results.push({ content, filename: "prettier.config.ts" })
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (tools.includes("stylelint")) {
|
|
194
|
+
const content = generateStylelintConfig()
|
|
195
|
+
|
|
196
|
+
results.push({ content, filename: "stylelint.config.js" })
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (tools.includes("cspell")) {
|
|
200
|
+
const content = generateCspellConfig()
|
|
201
|
+
|
|
202
|
+
results.push({ content, filename: "cspell.config.js" })
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (tools.includes("knip")) {
|
|
206
|
+
const content = generateKnipConfig()
|
|
207
|
+
|
|
208
|
+
results.push({ content, filename: "knip.config.ts" })
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
spinner.stop("Configuration files generated")
|
|
212
|
+
|
|
213
|
+
for (const { content, filename } of results) {
|
|
214
|
+
// eslint-disable-next-line no-await-in-loop -- Sequential writes for user feedback
|
|
215
|
+
await writeConfigFile(filename, content)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (shouldAddScripts) {
|
|
219
|
+
await updatePackageJsonScripts(tools, packageManager)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import * as clack from "@clack/prompts"
|
|
2
|
+
|
|
3
|
+
class SetupCancelledError extends Error {
|
|
4
|
+
constructor() {
|
|
5
|
+
super("Setup cancelled.")
|
|
6
|
+
this.name = "SetupCancelledError"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function promptToolSelection() {
|
|
11
|
+
const tools = await clack.multiselect({
|
|
12
|
+
initialValues: ["eslint", "prettier"],
|
|
13
|
+
message: "Which tools would you like to configure?",
|
|
14
|
+
options: [
|
|
15
|
+
{ label: "ESLint", value: "eslint" },
|
|
16
|
+
{ label: "Prettier", value: "prettier" },
|
|
17
|
+
{ label: "Stylelint", value: "stylelint" },
|
|
18
|
+
{ label: "CSpell", value: "cspell" },
|
|
19
|
+
{ label: "Knip", value: "knip" },
|
|
20
|
+
],
|
|
21
|
+
required: true,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
if (clack.isCancel(tools)) {
|
|
25
|
+
clack.cancel("Setup cancelled.")
|
|
26
|
+
|
|
27
|
+
throw new SetupCancelledError()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return tools
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function promptESLintOptions() {
|
|
34
|
+
const language = await clack.select({
|
|
35
|
+
message: "Which language are you using?",
|
|
36
|
+
options: [
|
|
37
|
+
{ label: "TypeScript", value: "typescript" },
|
|
38
|
+
{ label: "JavaScript only", value: "javascript" },
|
|
39
|
+
],
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
if (clack.isCancel(language)) {
|
|
43
|
+
clack.cancel("Setup cancelled.")
|
|
44
|
+
|
|
45
|
+
throw new SetupCancelledError()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let strictMode = false
|
|
49
|
+
|
|
50
|
+
if (language === "typescript") {
|
|
51
|
+
const strict = await clack.select({
|
|
52
|
+
message: "TypeScript strictness level?",
|
|
53
|
+
options: [
|
|
54
|
+
{ label: "Standard", value: "standard" },
|
|
55
|
+
{ label: "Strict (additional type-checked rules)", value: "strict" },
|
|
56
|
+
],
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
if (clack.isCancel(strict)) {
|
|
60
|
+
clack.cancel("Setup cancelled.")
|
|
61
|
+
|
|
62
|
+
throw new SetupCancelledError()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
strictMode = strict === "strict"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const framework = await clack.select({
|
|
69
|
+
message: "Which framework are you using?",
|
|
70
|
+
options: [
|
|
71
|
+
{ label: "None (Node.js)", value: "none" },
|
|
72
|
+
{ label: "React", value: "react" },
|
|
73
|
+
{ label: "Next.js", value: "next" },
|
|
74
|
+
],
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
if (clack.isCancel(framework)) {
|
|
78
|
+
clack.cancel("Setup cancelled.")
|
|
79
|
+
|
|
80
|
+
throw new SetupCancelledError()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let includeNode = framework === "none"
|
|
84
|
+
|
|
85
|
+
if (framework !== "none") {
|
|
86
|
+
const nodeBackend = await clack.confirm({
|
|
87
|
+
initialValue: false,
|
|
88
|
+
message: "Do you have a Node.js backend?",
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
if (clack.isCancel(nodeBackend)) {
|
|
92
|
+
clack.cancel("Setup cancelled.")
|
|
93
|
+
|
|
94
|
+
throw new SetupCancelledError()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
includeNode = nodeBackend
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const testing = await clack.multiselect({
|
|
101
|
+
message: "Which testing frameworks do you use?",
|
|
102
|
+
options: [
|
|
103
|
+
{ label: "Jest", value: "jest" },
|
|
104
|
+
{ label: "Vitest", value: "vitest" },
|
|
105
|
+
{ label: "Playwright", value: "playwright" },
|
|
106
|
+
],
|
|
107
|
+
required: false,
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
if (clack.isCancel(testing)) {
|
|
111
|
+
clack.cancel("Setup cancelled.")
|
|
112
|
+
|
|
113
|
+
throw new SetupCancelledError()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const extras = await clack.multiselect({
|
|
117
|
+
message: "Any additional configurations?",
|
|
118
|
+
options: [
|
|
119
|
+
{ label: "MobX", value: "mobx" },
|
|
120
|
+
{ label: "Storybook", value: "storybook" },
|
|
121
|
+
],
|
|
122
|
+
required: false,
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
if (clack.isCancel(extras)) {
|
|
126
|
+
clack.cancel("Setup cancelled.")
|
|
127
|
+
|
|
128
|
+
throw new SetupCancelledError()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const monorepo = await clack.confirm({
|
|
132
|
+
initialValue: false,
|
|
133
|
+
message: "Is this a monorepo?",
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
if (clack.isCancel(monorepo)) {
|
|
137
|
+
clack.cancel("Setup cancelled.")
|
|
138
|
+
|
|
139
|
+
throw new SetupCancelledError()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const isMonorepo = monorepo
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
extras,
|
|
146
|
+
framework,
|
|
147
|
+
includeNode,
|
|
148
|
+
isMonorepo,
|
|
149
|
+
language,
|
|
150
|
+
strictMode,
|
|
151
|
+
testing,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function promptOverwrite(filename) {
|
|
156
|
+
const overwrite = await clack.confirm({
|
|
157
|
+
initialValue: false,
|
|
158
|
+
message: `${filename} already exists. Overwrite?`,
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
if (clack.isCancel(overwrite)) {
|
|
162
|
+
clack.cancel("Setup cancelled.")
|
|
163
|
+
|
|
164
|
+
throw new SetupCancelledError()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return overwrite
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function promptInstall(packageManager, packages) {
|
|
171
|
+
const packageList = packages.join(", ")
|
|
172
|
+
|
|
173
|
+
const install = await clack.confirm({
|
|
174
|
+
initialValue: true,
|
|
175
|
+
message: `Install ${packageList} using ${packageManager}?`,
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
if (clack.isCancel(install)) {
|
|
179
|
+
return false
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return install
|
|
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
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** @type {import("cspell").FileSettings} */
|
|
2
|
+
const config = {
|
|
3
|
+
cache: {
|
|
4
|
+
cacheLocation: "./node_modules/.cache/cspell",
|
|
5
|
+
useCache: true,
|
|
6
|
+
},
|
|
7
|
+
caseSensitive: false,
|
|
8
|
+
dictionaries: ["shared"],
|
|
9
|
+
dictionaryDefinitions: [
|
|
10
|
+
{
|
|
11
|
+
name: "shared",
|
|
12
|
+
path: "./node_modules/@dvukovic/style-guide/cspell/base.txt",
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
useGitignore: true,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default config
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as core, default } from "./configs/core.js"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as core } from "./configs/core.js"
|