@verekia/warden 0.0.2 → 0.0.3
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/package.json +2 -66
- package/src/checks/config-files-present.ts +3 -1
- package/src/checks/exact-dependency-versions.ts +6 -1
- package/src/checks/matching-dependency-versions.ts +3 -1
- package/src/checks/next-config.ts +11 -2
- package/src/checks/portless-next-dev.ts +5 -3
- package/src/checks/required-scripts.ts +8 -1
- package/src/checks/test-script-consistency.ts +9 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verekia/warden",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Linter that checks repositories share consistent configs and tool versions.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"warden": "./src/index.ts"
|
|
@@ -37,75 +37,11 @@
|
|
|
37
37
|
"../svg-to-tsl"
|
|
38
38
|
],
|
|
39
39
|
"checks": {
|
|
40
|
-
"configFilesPresent": {
|
|
41
|
-
"enabled": true,
|
|
42
|
-
"files": [
|
|
43
|
-
".oxfmtrc.json",
|
|
44
|
-
".oxlintrc.json"
|
|
45
|
-
]
|
|
46
|
-
},
|
|
47
|
-
"matchingDependencyVersions": {
|
|
48
|
-
"enabled": true,
|
|
49
|
-
"packages": [
|
|
50
|
-
"oxfmt",
|
|
51
|
-
"oxlint"
|
|
52
|
-
]
|
|
53
|
-
},
|
|
54
|
-
"exactDependencyVersions": {
|
|
55
|
-
"enabled": true,
|
|
56
|
-
"packages": {
|
|
57
|
-
"oxfmt": "0.48.0",
|
|
58
|
-
"oxlint": "1.63.0"
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
"requiredScripts": {
|
|
62
|
-
"enabled": true,
|
|
63
|
-
"scripts": {
|
|
64
|
-
"format": "oxfmt .",
|
|
65
|
-
"format:check": "oxfmt --check .",
|
|
66
|
-
"lint": "oxlint .",
|
|
67
|
-
"lint:fix": "oxlint --fix ."
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
40
|
"typecheckScript": {
|
|
71
|
-
"enabled": true,
|
|
72
41
|
"modes": {
|
|
42
|
+
"gputex": "bun-filter-all",
|
|
73
43
|
"nanothree": "bun-filter-all"
|
|
74
44
|
}
|
|
75
|
-
},
|
|
76
|
-
"testScriptConsistency": {
|
|
77
|
-
"enabled": true,
|
|
78
|
-
"testFilePattern": "**/*.test.{ts,tsx,js,jsx}",
|
|
79
|
-
"testScript": "bun test",
|
|
80
|
-
"allWithoutTests": "bun run format:check && bun run lint && bun run typecheck && bun run warden",
|
|
81
|
-
"allWithTests": "bun run format:check && bun run lint && bun run typecheck && bun run warden && bun run test"
|
|
82
|
-
},
|
|
83
|
-
"tailwindOxfmtConfig": {
|
|
84
|
-
"enabled": true
|
|
85
|
-
},
|
|
86
|
-
"portlessNextDev": {
|
|
87
|
-
"enabled": true,
|
|
88
|
-
"version": "0.13.0"
|
|
89
|
-
},
|
|
90
|
-
"nextConfig": {
|
|
91
|
-
"enabled": true,
|
|
92
|
-
"options": {
|
|
93
|
-
"reactStrictMode": true,
|
|
94
|
-
"reactCompiler": true,
|
|
95
|
-
"output": "export"
|
|
96
|
-
},
|
|
97
|
-
"devDependencies": {
|
|
98
|
-
"babel-plugin-react-compiler": "1.0.0"
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
"wardenScript": {
|
|
102
|
-
"enabled": true,
|
|
103
|
-
"package": "@verekia/warden"
|
|
104
|
-
},
|
|
105
|
-
"ciWorkflow": {
|
|
106
|
-
"enabled": true,
|
|
107
|
-
"file": ".github/workflows/ci.yml",
|
|
108
|
-
"runs": "bun run all"
|
|
109
45
|
}
|
|
110
46
|
}
|
|
111
47
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { CheckResult, Project, WardenConfig } from '../types.ts'
|
|
2
2
|
|
|
3
|
+
const DEFAULT_FILES = ['.oxfmtrc.json', '.oxlintrc.json']
|
|
4
|
+
|
|
3
5
|
export const configFilesPresent = async (
|
|
4
6
|
projects: Project[],
|
|
5
7
|
options: NonNullable<NonNullable<WardenConfig['checks']>['configFilesPresent']>,
|
|
@@ -7,7 +9,7 @@ export const configFilesPresent = async (
|
|
|
7
9
|
const messages: string[] = []
|
|
8
10
|
let passed = true
|
|
9
11
|
|
|
10
|
-
const files = options.files ??
|
|
12
|
+
const files = options.files ?? DEFAULT_FILES
|
|
11
13
|
for (const project of projects) {
|
|
12
14
|
for (const file of files) {
|
|
13
15
|
const exists = await Bun.file(`${project.path}/${file}`).exists()
|
|
@@ -5,6 +5,11 @@ const findVersion = (pkg: PackageJson, name: string): string | undefined =>
|
|
|
5
5
|
|
|
6
6
|
const EXACT_SEMVER = /^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/
|
|
7
7
|
|
|
8
|
+
const DEFAULT_PACKAGES: Record<string, string> = {
|
|
9
|
+
oxfmt: '0.48.0',
|
|
10
|
+
oxlint: '1.63.0',
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
export const exactDependencyVersions = (
|
|
9
14
|
projects: Project[],
|
|
10
15
|
options: NonNullable<NonNullable<WardenConfig['checks']>['exactDependencyVersions']>,
|
|
@@ -12,7 +17,7 @@ export const exactDependencyVersions = (
|
|
|
12
17
|
const messages: string[] = []
|
|
13
18
|
let passed = true
|
|
14
19
|
|
|
15
|
-
const packages = Object.entries(options.packages ??
|
|
20
|
+
const packages = Object.entries(options.packages ?? DEFAULT_PACKAGES)
|
|
16
21
|
if (packages.length === 0) {
|
|
17
22
|
return { name: 'exactDependencyVersions', passed, messages }
|
|
18
23
|
}
|
|
@@ -3,6 +3,8 @@ import type { CheckResult, PackageJson, Project, WardenConfig } from '../types.t
|
|
|
3
3
|
const findVersion = (pkg: PackageJson, name: string): string | undefined =>
|
|
4
4
|
pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? pkg.peerDependencies?.[name]
|
|
5
5
|
|
|
6
|
+
const DEFAULT_PACKAGES = ['oxfmt', 'oxlint']
|
|
7
|
+
|
|
6
8
|
export const matchingDependencyVersions = (
|
|
7
9
|
projects: Project[],
|
|
8
10
|
options: NonNullable<NonNullable<WardenConfig['checks']>['matchingDependencyVersions']>,
|
|
@@ -10,7 +12,7 @@ export const matchingDependencyVersions = (
|
|
|
10
12
|
const messages: string[] = []
|
|
11
13
|
let passed = true
|
|
12
14
|
|
|
13
|
-
for (const packageName of options.packages ??
|
|
15
|
+
for (const packageName of options.packages ?? DEFAULT_PACKAGES) {
|
|
14
16
|
const versionToProjects = new Map<string, string[]>()
|
|
15
17
|
const missing: string[] = []
|
|
16
18
|
|
|
@@ -6,6 +6,15 @@ import type { CheckResult, PackageJson, Project, WardenConfig } from '../types.t
|
|
|
6
6
|
const NEXT = 'next'
|
|
7
7
|
const CONFIG_VARIANTS = ['next.config.js', 'next.config.mjs', 'next.config.ts', 'next.config.cjs'] as const
|
|
8
8
|
|
|
9
|
+
const DEFAULT_OPTIONS: Record<string, unknown> = {
|
|
10
|
+
reactStrictMode: true,
|
|
11
|
+
reactCompiler: true,
|
|
12
|
+
output: 'export',
|
|
13
|
+
}
|
|
14
|
+
const DEFAULT_DEV_DEPENDENCIES: Record<string, string> = {
|
|
15
|
+
'babel-plugin-react-compiler': '1.0.0',
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
type WorkspaceField = string[] | { packages?: string[] } | undefined
|
|
10
19
|
|
|
11
20
|
const dependsOnNext = (pkg: PackageJson): boolean =>
|
|
@@ -106,7 +115,7 @@ export const nextConfig = async (
|
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
if (cfg) {
|
|
109
|
-
for (const [key, expected] of Object.entries(options.options ??
|
|
118
|
+
for (const [key, expected] of Object.entries(options.options ?? DEFAULT_OPTIONS)) {
|
|
110
119
|
if (!equal(cfg[key], expected)) {
|
|
111
120
|
passed = false
|
|
112
121
|
const actual = key in cfg ? JSON.stringify(cfg[key]) : 'missing'
|
|
@@ -118,7 +127,7 @@ export const nextConfig = async (
|
|
|
118
127
|
}
|
|
119
128
|
}
|
|
120
129
|
|
|
121
|
-
for (const [name, expected] of Object.entries(options.devDependencies ??
|
|
130
|
+
for (const [name, expected] of Object.entries(options.devDependencies ?? DEFAULT_DEV_DEPENDENCIES)) {
|
|
122
131
|
const declared = pkg.devDependencies?.[name]
|
|
123
132
|
if (declared !== expected) {
|
|
124
133
|
const actual = declared === undefined ? 'missing' : JSON.stringify(declared)
|
|
@@ -5,6 +5,7 @@ import type { CheckResult, PackageJson, Project, WardenConfig } from '../types.t
|
|
|
5
5
|
|
|
6
6
|
const NEXT_DEV_RE = /\bnext dev\b/
|
|
7
7
|
const PORTLESS_WRAP_RE = /\bportless\s+\S+\s+next dev\b/
|
|
8
|
+
const DEFAULT_VERSION = '0.13.0'
|
|
8
9
|
|
|
9
10
|
type WorkspaceField = string[] | { packages?: string[] } | undefined
|
|
10
11
|
|
|
@@ -66,13 +67,14 @@ export const portlessNextDev = async (
|
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
if (usesNextDev
|
|
70
|
+
if (usesNextDev) {
|
|
71
|
+
const expectedVersion = options.version ?? DEFAULT_VERSION
|
|
70
72
|
const declared = pkg.devDependencies?.portless
|
|
71
|
-
if (declared !==
|
|
73
|
+
if (declared !== expectedVersion) {
|
|
72
74
|
const actual = declared === undefined ? 'missing' : JSON.stringify(declared)
|
|
73
75
|
passed = false
|
|
74
76
|
messages.push(
|
|
75
|
-
`${project.name}/${relPath}: uses "next dev" but devDependencies.portless is ${actual} — expected exact "${
|
|
77
|
+
`${project.name}/${relPath}: uses "next dev" but devDependencies.portless is ${actual} — expected exact "${expectedVersion}" (must be declared in this package.json)`,
|
|
76
78
|
)
|
|
77
79
|
}
|
|
78
80
|
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { CheckResult, Project, WardenConfig } from '../types.ts'
|
|
2
2
|
|
|
3
|
+
const DEFAULT_SCRIPTS: Record<string, string> = {
|
|
4
|
+
format: 'oxfmt .',
|
|
5
|
+
'format:check': 'oxfmt --check .',
|
|
6
|
+
lint: 'oxlint .',
|
|
7
|
+
'lint:fix': 'oxlint --fix .',
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
export const requiredScripts = (
|
|
4
11
|
projects: Project[],
|
|
5
12
|
options: NonNullable<NonNullable<WardenConfig['checks']>['requiredScripts']>,
|
|
@@ -7,7 +14,7 @@ export const requiredScripts = (
|
|
|
7
14
|
const messages: string[] = []
|
|
8
15
|
let passed = true
|
|
9
16
|
|
|
10
|
-
const required = Object.entries(options.scripts ??
|
|
17
|
+
const required = Object.entries(options.scripts ?? DEFAULT_SCRIPTS)
|
|
11
18
|
if (required.length === 0) {
|
|
12
19
|
return { name: 'requiredScripts', passed, messages }
|
|
13
20
|
}
|
|
@@ -13,6 +13,9 @@ const projectHasTests = async (projectPath: string, pattern: string): Promise<bo
|
|
|
13
13
|
|
|
14
14
|
const DEFAULT_TEST_FILE_PATTERN = '**/*.test.{ts,tsx,js,jsx}'
|
|
15
15
|
const DEFAULT_TEST_SCRIPT = 'bun test'
|
|
16
|
+
const DEFAULT_ALL_WITHOUT_TESTS = 'bun run format:check && bun run lint && bun run typecheck && bun run warden'
|
|
17
|
+
const DEFAULT_ALL_WITH_TESTS =
|
|
18
|
+
'bun run format:check && bun run lint && bun run typecheck && bun run warden && bun run test'
|
|
16
19
|
|
|
17
20
|
export const testScriptConsistency = async (
|
|
18
21
|
projects: Project[],
|
|
@@ -23,6 +26,8 @@ export const testScriptConsistency = async (
|
|
|
23
26
|
|
|
24
27
|
const testFilePattern = options.testFilePattern ?? DEFAULT_TEST_FILE_PATTERN
|
|
25
28
|
const testScript = options.testScript ?? DEFAULT_TEST_SCRIPT
|
|
29
|
+
const allWithTests = options.allWithTests ?? DEFAULT_ALL_WITH_TESTS
|
|
30
|
+
const allWithoutTests = options.allWithoutTests ?? DEFAULT_ALL_WITHOUT_TESTS
|
|
26
31
|
|
|
27
32
|
for (const project of projects) {
|
|
28
33
|
const scripts = project.packageJson?.scripts ?? {}
|
|
@@ -35,17 +40,17 @@ export const testScriptConsistency = async (
|
|
|
35
40
|
`${project.name}: has test files but "test" script is ${JSON.stringify(scripts.test)}, expected ${JSON.stringify(testScript)}`,
|
|
36
41
|
)
|
|
37
42
|
}
|
|
38
|
-
if (
|
|
43
|
+
if (scripts.all !== allWithTests) {
|
|
39
44
|
passed = false
|
|
40
45
|
messages.push(
|
|
41
|
-
`${project.name}: "all" is ${JSON.stringify(scripts.all)}, expected ${JSON.stringify(
|
|
46
|
+
`${project.name}: "all" is ${JSON.stringify(scripts.all)}, expected ${JSON.stringify(allWithTests)} (project has tests)`,
|
|
42
47
|
)
|
|
43
48
|
}
|
|
44
49
|
} else {
|
|
45
|
-
if (
|
|
50
|
+
if (scripts.all !== allWithoutTests) {
|
|
46
51
|
passed = false
|
|
47
52
|
messages.push(
|
|
48
|
-
`${project.name}: "all" is ${JSON.stringify(scripts.all)}, expected ${JSON.stringify(
|
|
53
|
+
`${project.name}: "all" is ${JSON.stringify(scripts.all)}, expected ${JSON.stringify(allWithoutTests)} (no tests detected)`,
|
|
49
54
|
)
|
|
50
55
|
}
|
|
51
56
|
}
|