@verekia/warden 0.0.0 → 0.0.1
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 +1 -1
- package/src/checks/config-files-present.ts +3 -2
- package/src/checks/exact-dependency-versions.ts +7 -2
- package/src/checks/matching-dependency-versions.ts +2 -2
- package/src/checks/next-config.ts +3 -3
- package/src/checks/portless-next-dev.ts +2 -2
- package/src/checks/required-scripts.ts +7 -2
- package/src/checks/tailwind-oxfmt-config.ts +1 -1
- package/src/checks/test-script-consistency.ts +12 -6
- package/src/checks/typecheck-script.ts +1 -1
- package/src/checks/warden-script.ts +7 -4
- package/src/config.ts +1 -4
- package/src/index.ts +22 -20
- package/src/types.ts +17 -17
package/package.json
CHANGED
|
@@ -2,13 +2,14 @@ import type { CheckResult, Project, WardenConfig } from '../types.ts'
|
|
|
2
2
|
|
|
3
3
|
export const configFilesPresent = async (
|
|
4
4
|
projects: Project[],
|
|
5
|
-
options: NonNullable<WardenConfig['checks']['configFilesPresent']>,
|
|
5
|
+
options: NonNullable<NonNullable<WardenConfig['checks']>['configFilesPresent']>,
|
|
6
6
|
): Promise<CheckResult> => {
|
|
7
7
|
const messages: string[] = []
|
|
8
8
|
let passed = true
|
|
9
9
|
|
|
10
|
+
const files = options.files ?? []
|
|
10
11
|
for (const project of projects) {
|
|
11
|
-
for (const file of
|
|
12
|
+
for (const file of files) {
|
|
12
13
|
const exists = await Bun.file(`${project.path}/${file}`).exists()
|
|
13
14
|
if (!exists) {
|
|
14
15
|
passed = false
|
|
@@ -7,18 +7,23 @@ const EXACT_SEMVER = /^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/
|
|
|
7
7
|
|
|
8
8
|
export const exactDependencyVersions = (
|
|
9
9
|
projects: Project[],
|
|
10
|
-
options: NonNullable<WardenConfig['checks']['exactDependencyVersions']>,
|
|
10
|
+
options: NonNullable<NonNullable<WardenConfig['checks']>['exactDependencyVersions']>,
|
|
11
11
|
): CheckResult => {
|
|
12
12
|
const messages: string[] = []
|
|
13
13
|
let passed = true
|
|
14
14
|
|
|
15
|
+
const packages = Object.entries(options.packages ?? {})
|
|
16
|
+
if (packages.length === 0) {
|
|
17
|
+
return { name: 'exactDependencyVersions', passed, messages }
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
for (const project of projects) {
|
|
16
21
|
if (!project.packageJson) {
|
|
17
22
|
passed = false
|
|
18
23
|
messages.push(`${project.name}: no package.json`)
|
|
19
24
|
continue
|
|
20
25
|
}
|
|
21
|
-
for (const [name, expected] of
|
|
26
|
+
for (const [name, expected] of packages) {
|
|
22
27
|
const declared = findVersion(project.packageJson, name)
|
|
23
28
|
if (declared === undefined) {
|
|
24
29
|
passed = false
|
|
@@ -5,12 +5,12 @@ const findVersion = (pkg: PackageJson, name: string): string | undefined =>
|
|
|
5
5
|
|
|
6
6
|
export const matchingDependencyVersions = (
|
|
7
7
|
projects: Project[],
|
|
8
|
-
options: NonNullable<WardenConfig['checks']['matchingDependencyVersions']>,
|
|
8
|
+
options: NonNullable<NonNullable<WardenConfig['checks']>['matchingDependencyVersions']>,
|
|
9
9
|
): CheckResult => {
|
|
10
10
|
const messages: string[] = []
|
|
11
11
|
let passed = true
|
|
12
12
|
|
|
13
|
-
for (const packageName of options.packages) {
|
|
13
|
+
for (const packageName of options.packages ?? []) {
|
|
14
14
|
const versionToProjects = new Map<string, string[]>()
|
|
15
15
|
const missing: string[] = []
|
|
16
16
|
|
|
@@ -66,7 +66,7 @@ const loadConfigObject = async (absPath: string): Promise<Record<string, unknown
|
|
|
66
66
|
|
|
67
67
|
export const nextConfig = async (
|
|
68
68
|
projects: Project[],
|
|
69
|
-
options: NonNullable<WardenConfig['checks']['nextConfig']>,
|
|
69
|
+
options: NonNullable<NonNullable<WardenConfig['checks']>['nextConfig']>,
|
|
70
70
|
): Promise<CheckResult> => {
|
|
71
71
|
const messages: string[] = []
|
|
72
72
|
let passed = true
|
|
@@ -106,7 +106,7 @@ export const nextConfig = async (
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
if (cfg) {
|
|
109
|
-
for (const [key, expected] of Object.entries(options.options)) {
|
|
109
|
+
for (const [key, expected] of Object.entries(options.options ?? {})) {
|
|
110
110
|
if (!equal(cfg[key], expected)) {
|
|
111
111
|
passed = false
|
|
112
112
|
const actual = key in cfg ? JSON.stringify(cfg[key]) : 'missing'
|
|
@@ -118,7 +118,7 @@ export const nextConfig = async (
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
for (const [name, expected] of Object.entries(options.devDependencies)) {
|
|
121
|
+
for (const [name, expected] of Object.entries(options.devDependencies ?? {})) {
|
|
122
122
|
const declared = pkg.devDependencies?.[name]
|
|
123
123
|
if (declared !== expected) {
|
|
124
124
|
const actual = declared === undefined ? 'missing' : JSON.stringify(declared)
|
|
@@ -43,7 +43,7 @@ const collectPackageJsons = async (project: Project): Promise<Array<{ relPath: s
|
|
|
43
43
|
|
|
44
44
|
export const portlessNextDev = async (
|
|
45
45
|
projects: Project[],
|
|
46
|
-
options: NonNullable<WardenConfig['checks']['portlessNextDev']>,
|
|
46
|
+
options: NonNullable<NonNullable<WardenConfig['checks']>['portlessNextDev']>,
|
|
47
47
|
): Promise<CheckResult> => {
|
|
48
48
|
const messages: string[] = []
|
|
49
49
|
let passed = true
|
|
@@ -66,7 +66,7 @@ export const portlessNextDev = async (
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
if (usesNextDev) {
|
|
69
|
+
if (usesNextDev && options.version !== undefined) {
|
|
70
70
|
const declared = pkg.devDependencies?.portless
|
|
71
71
|
if (declared !== options.version) {
|
|
72
72
|
const actual = declared === undefined ? 'missing' : JSON.stringify(declared)
|
|
@@ -2,11 +2,16 @@ import type { CheckResult, Project, WardenConfig } from '../types.ts'
|
|
|
2
2
|
|
|
3
3
|
export const requiredScripts = (
|
|
4
4
|
projects: Project[],
|
|
5
|
-
options: NonNullable<WardenConfig['checks']['requiredScripts']>,
|
|
5
|
+
options: NonNullable<NonNullable<WardenConfig['checks']>['requiredScripts']>,
|
|
6
6
|
): CheckResult => {
|
|
7
7
|
const messages: string[] = []
|
|
8
8
|
let passed = true
|
|
9
9
|
|
|
10
|
+
const required = Object.entries(options.scripts ?? {})
|
|
11
|
+
if (required.length === 0) {
|
|
12
|
+
return { name: 'requiredScripts', passed, messages }
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
for (const project of projects) {
|
|
11
16
|
const scripts = project.packageJson?.scripts
|
|
12
17
|
if (!scripts) {
|
|
@@ -14,7 +19,7 @@ export const requiredScripts = (
|
|
|
14
19
|
messages.push(`${project.name}: no "scripts" in package.json`)
|
|
15
20
|
continue
|
|
16
21
|
}
|
|
17
|
-
for (const [name, expected] of
|
|
22
|
+
for (const [name, expected] of required) {
|
|
18
23
|
const actual = scripts[name]
|
|
19
24
|
if (actual === undefined) {
|
|
20
25
|
passed = false
|
|
@@ -11,7 +11,7 @@ const dependsOnTailwind = (pkg: PackageJson): boolean =>
|
|
|
11
11
|
|
|
12
12
|
export const tailwindOxfmtConfig = async (
|
|
13
13
|
projects: Project[],
|
|
14
|
-
_options: NonNullable<WardenConfig['checks']['tailwindOxfmtConfig']>,
|
|
14
|
+
_options: NonNullable<NonNullable<WardenConfig['checks']>['tailwindOxfmtConfig']>,
|
|
15
15
|
): Promise<CheckResult> => {
|
|
16
16
|
const messages: string[] = []
|
|
17
17
|
let passed = true
|
|
@@ -11,32 +11,38 @@ const projectHasTests = async (projectPath: string, pattern: string): Promise<bo
|
|
|
11
11
|
return false
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
const DEFAULT_TEST_FILE_PATTERN = '**/*.test.{ts,tsx,js,jsx}'
|
|
15
|
+
const DEFAULT_TEST_SCRIPT = 'bun test'
|
|
16
|
+
|
|
14
17
|
export const testScriptConsistency = async (
|
|
15
18
|
projects: Project[],
|
|
16
|
-
options: NonNullable<WardenConfig['checks']['testScriptConsistency']>,
|
|
19
|
+
options: NonNullable<NonNullable<WardenConfig['checks']>['testScriptConsistency']>,
|
|
17
20
|
): Promise<CheckResult> => {
|
|
18
21
|
const messages: string[] = []
|
|
19
22
|
let passed = true
|
|
20
23
|
|
|
24
|
+
const testFilePattern = options.testFilePattern ?? DEFAULT_TEST_FILE_PATTERN
|
|
25
|
+
const testScript = options.testScript ?? DEFAULT_TEST_SCRIPT
|
|
26
|
+
|
|
21
27
|
for (const project of projects) {
|
|
22
28
|
const scripts = project.packageJson?.scripts ?? {}
|
|
23
|
-
const hasTests = await projectHasTests(project.path,
|
|
29
|
+
const hasTests = await projectHasTests(project.path, testFilePattern)
|
|
24
30
|
|
|
25
31
|
if (hasTests) {
|
|
26
|
-
if (scripts.test !==
|
|
32
|
+
if (scripts.test !== testScript) {
|
|
27
33
|
passed = false
|
|
28
34
|
messages.push(
|
|
29
|
-
`${project.name}: has test files but "test" script is ${JSON.stringify(scripts.test)}, expected ${JSON.stringify(
|
|
35
|
+
`${project.name}: has test files but "test" script is ${JSON.stringify(scripts.test)}, expected ${JSON.stringify(testScript)}`,
|
|
30
36
|
)
|
|
31
37
|
}
|
|
32
|
-
if (scripts.all !== options.allWithTests) {
|
|
38
|
+
if (options.allWithTests !== undefined && scripts.all !== options.allWithTests) {
|
|
33
39
|
passed = false
|
|
34
40
|
messages.push(
|
|
35
41
|
`${project.name}: "all" is ${JSON.stringify(scripts.all)}, expected ${JSON.stringify(options.allWithTests)} (project has tests)`,
|
|
36
42
|
)
|
|
37
43
|
}
|
|
38
44
|
} else {
|
|
39
|
-
if (scripts.all !== options.allWithoutTests) {
|
|
45
|
+
if (options.allWithoutTests !== undefined && scripts.all !== options.allWithoutTests) {
|
|
40
46
|
passed = false
|
|
41
47
|
messages.push(
|
|
42
48
|
`${project.name}: "all" is ${JSON.stringify(scripts.all)}, expected ${JSON.stringify(options.allWithoutTests)} (no tests detected)`,
|
|
@@ -17,7 +17,7 @@ const readTsconfigReferences = async (projectPath: string): Promise<boolean> =>
|
|
|
17
17
|
|
|
18
18
|
export const typecheckScript = async (
|
|
19
19
|
projects: Project[],
|
|
20
|
-
options: NonNullable<WardenConfig['checks']['typecheckScript']>,
|
|
20
|
+
options: NonNullable<NonNullable<WardenConfig['checks']>['typecheckScript']>,
|
|
21
21
|
): Promise<CheckResult> => {
|
|
22
22
|
const messages: string[] = []
|
|
23
23
|
let passed = true
|
|
@@ -3,28 +3,31 @@ import type { CheckResult, PackageJson, Project, WardenConfig } from '../types.t
|
|
|
3
3
|
const dependsOn = (pkg: PackageJson, name: string): boolean =>
|
|
4
4
|
name in (pkg.dependencies ?? {}) || name in (pkg.devDependencies ?? {}) || name in (pkg.peerDependencies ?? {})
|
|
5
5
|
|
|
6
|
+
const DEFAULT_PACKAGE = '@verekia/warden'
|
|
7
|
+
|
|
6
8
|
export const wardenScript = (
|
|
7
9
|
projects: Project[],
|
|
8
|
-
options: NonNullable<WardenConfig['checks']['wardenScript']>,
|
|
10
|
+
options: NonNullable<NonNullable<WardenConfig['checks']>['wardenScript']>,
|
|
9
11
|
): CheckResult => {
|
|
10
12
|
const messages: string[] = []
|
|
11
13
|
let passed = true
|
|
14
|
+
const packageName = options.package ?? DEFAULT_PACKAGE
|
|
12
15
|
|
|
13
16
|
for (const project of projects) {
|
|
14
17
|
const pkg = project.packageJson
|
|
15
|
-
if (!pkg || !dependsOn(pkg,
|
|
18
|
+
if (!pkg || !dependsOn(pkg, packageName)) continue
|
|
16
19
|
|
|
17
20
|
const scripts = pkg.scripts ?? {}
|
|
18
21
|
if (scripts.warden !== 'warden') {
|
|
19
22
|
passed = false
|
|
20
23
|
messages.push(
|
|
21
|
-
`${project.name}: depends on ${
|
|
24
|
+
`${project.name}: depends on ${packageName} but "warden" script is ${JSON.stringify(scripts.warden)}, expected "warden"`,
|
|
22
25
|
)
|
|
23
26
|
}
|
|
24
27
|
if (!scripts.all || !scripts.all.includes('bun run warden')) {
|
|
25
28
|
passed = false
|
|
26
29
|
messages.push(
|
|
27
|
-
`${project.name}: depends on ${
|
|
30
|
+
`${project.name}: depends on ${packageName} but "all" script ${JSON.stringify(scripts.all)} is missing "bun run warden"`,
|
|
28
31
|
)
|
|
29
32
|
}
|
|
30
33
|
}
|
package/src/config.ts
CHANGED
|
@@ -9,10 +9,7 @@ export const loadConfig = async (baseDir: string): Promise<WardenConfig> => {
|
|
|
9
9
|
throw new Error(`No package.json found at ${packageJsonPath}`)
|
|
10
10
|
}
|
|
11
11
|
const pkg = (await packageJsonFile.json()) as PackageJson & { warden?: WardenConfig }
|
|
12
|
-
|
|
13
|
-
throw new Error(`No "warden" key in ${packageJsonPath}`)
|
|
14
|
-
}
|
|
15
|
-
return pkg.warden
|
|
12
|
+
return pkg.warden ?? {}
|
|
16
13
|
}
|
|
17
14
|
|
|
18
15
|
export const resolveProjects = async (config: WardenConfig, baseDir: string): Promise<Project[]> => {
|
package/src/index.ts
CHANGED
|
@@ -46,36 +46,38 @@ const scopeNote = filterNames.length > 0 ? ` (filtered from ${allProjects.length
|
|
|
46
46
|
console.log(`warden — checking ${projects.length} project(s)${scopeNote}: ${projects.map(p => p.name).join(', ')}\n`)
|
|
47
47
|
|
|
48
48
|
const results: CheckResult[] = []
|
|
49
|
+
const checks = config.checks ?? {}
|
|
50
|
+
const enabled = (c: { enabled?: boolean } | undefined): boolean => c?.enabled !== false
|
|
49
51
|
|
|
50
|
-
if (
|
|
51
|
-
results.push(await configFilesPresent(projects,
|
|
52
|
+
if (enabled(checks.configFilesPresent)) {
|
|
53
|
+
results.push(await configFilesPresent(projects, checks.configFilesPresent ?? {}))
|
|
52
54
|
}
|
|
53
|
-
if (
|
|
54
|
-
results.push(matchingDependencyVersions(projects,
|
|
55
|
+
if (enabled(checks.matchingDependencyVersions)) {
|
|
56
|
+
results.push(matchingDependencyVersions(projects, checks.matchingDependencyVersions ?? {}))
|
|
55
57
|
}
|
|
56
|
-
if (
|
|
57
|
-
results.push(exactDependencyVersions(projects,
|
|
58
|
+
if (enabled(checks.exactDependencyVersions)) {
|
|
59
|
+
results.push(exactDependencyVersions(projects, checks.exactDependencyVersions ?? {}))
|
|
58
60
|
}
|
|
59
|
-
if (
|
|
60
|
-
results.push(requiredScripts(projects,
|
|
61
|
+
if (enabled(checks.requiredScripts)) {
|
|
62
|
+
results.push(requiredScripts(projects, checks.requiredScripts ?? {}))
|
|
61
63
|
}
|
|
62
|
-
if (
|
|
63
|
-
results.push(await typecheckScript(projects,
|
|
64
|
+
if (enabled(checks.typecheckScript)) {
|
|
65
|
+
results.push(await typecheckScript(projects, checks.typecheckScript ?? {}))
|
|
64
66
|
}
|
|
65
|
-
if (
|
|
66
|
-
results.push(await testScriptConsistency(projects,
|
|
67
|
+
if (enabled(checks.testScriptConsistency)) {
|
|
68
|
+
results.push(await testScriptConsistency(projects, checks.testScriptConsistency ?? {}))
|
|
67
69
|
}
|
|
68
|
-
if (
|
|
69
|
-
results.push(await tailwindOxfmtConfig(projects,
|
|
70
|
+
if (enabled(checks.tailwindOxfmtConfig)) {
|
|
71
|
+
results.push(await tailwindOxfmtConfig(projects, checks.tailwindOxfmtConfig ?? {}))
|
|
70
72
|
}
|
|
71
|
-
if (
|
|
72
|
-
results.push(await portlessNextDev(projects,
|
|
73
|
+
if (enabled(checks.portlessNextDev)) {
|
|
74
|
+
results.push(await portlessNextDev(projects, checks.portlessNextDev ?? {}))
|
|
73
75
|
}
|
|
74
|
-
if (
|
|
75
|
-
results.push(await nextConfig(projects,
|
|
76
|
+
if (enabled(checks.nextConfig)) {
|
|
77
|
+
results.push(await nextConfig(projects, checks.nextConfig ?? {}))
|
|
76
78
|
}
|
|
77
|
-
if (
|
|
78
|
-
results.push(wardenScript(projects,
|
|
79
|
+
if (enabled(checks.wardenScript)) {
|
|
80
|
+
results.push(wardenScript(projects, checks.wardenScript ?? {}))
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
for (const result of results) {
|
package/src/types.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
export type WardenConfig = {
|
|
2
2
|
projects?: string[]
|
|
3
|
-
checks
|
|
4
|
-
configFilesPresent?: { enabled
|
|
5
|
-
matchingDependencyVersions?: { enabled
|
|
6
|
-
exactDependencyVersions?: { enabled
|
|
7
|
-
requiredScripts?: { enabled
|
|
8
|
-
typecheckScript?: { enabled
|
|
3
|
+
checks?: {
|
|
4
|
+
configFilesPresent?: { enabled?: boolean; files?: string[] }
|
|
5
|
+
matchingDependencyVersions?: { enabled?: boolean; packages?: string[] }
|
|
6
|
+
exactDependencyVersions?: { enabled?: boolean; packages?: Record<string, string> }
|
|
7
|
+
requiredScripts?: { enabled?: boolean; scripts?: Record<string, string> }
|
|
8
|
+
typecheckScript?: { enabled?: boolean; modes?: Record<string, 'auto' | 'bun-filter-all'> }
|
|
9
9
|
testScriptConsistency?: {
|
|
10
|
-
enabled
|
|
11
|
-
testFilePattern
|
|
12
|
-
testScript
|
|
13
|
-
allWithoutTests
|
|
14
|
-
allWithTests
|
|
10
|
+
enabled?: boolean
|
|
11
|
+
testFilePattern?: string
|
|
12
|
+
testScript?: string
|
|
13
|
+
allWithoutTests?: string
|
|
14
|
+
allWithTests?: string
|
|
15
15
|
}
|
|
16
|
-
tailwindOxfmtConfig?: { enabled
|
|
17
|
-
portlessNextDev?: { enabled
|
|
16
|
+
tailwindOxfmtConfig?: { enabled?: boolean }
|
|
17
|
+
portlessNextDev?: { enabled?: boolean; version?: string }
|
|
18
18
|
nextConfig?: {
|
|
19
|
-
enabled
|
|
20
|
-
options
|
|
21
|
-
devDependencies
|
|
19
|
+
enabled?: boolean
|
|
20
|
+
options?: Record<string, unknown>
|
|
21
|
+
devDependencies?: Record<string, string>
|
|
22
22
|
}
|
|
23
|
-
wardenScript?: { enabled
|
|
23
|
+
wardenScript?: { enabled?: boolean; package?: string }
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|