@ankhorage/devtools 1.0.2 → 1.0.4

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,11 +1,13 @@
1
1
  # devtools
2
2
 
3
- Shared ESLint and Prettier configuration for modern TypeScript projects.
3
+ Shared ESLint, Prettier, and Knip configuration for modern TypeScript projects.
4
4
 
5
5
  ## What you get
6
6
 
7
7
  - Consistent linting across repos
8
8
  - Zero-config Prettier setup
9
+ - Shared Knip static-analysis defaults
10
+ - Bundled tool binaries for ESLint, Prettier, and Knip
9
11
  - Strict TypeScript rules without compromise
10
12
  - One source of truth for tooling
11
13
 
@@ -14,6 +16,8 @@ Shared ESLint and Prettier configuration for modern TypeScript projects.
14
16
  - Flat ESLint config (latest standard)
15
17
  - Preconfigured plugin ecosystem
16
18
  - Prettier integration
19
+ - Shared Knip config factory
20
+ - `ankhorage-eslint`, `ankhorage-prettier`, and `ankhorage-knip` binaries
17
21
  - Monorepo-friendly
18
22
 
19
23
  ## Installation
@@ -22,22 +26,105 @@ Shared ESLint and Prettier configuration for modern TypeScript projects.
22
26
  bun add -D @ankhorage/devtools
23
27
  ```
24
28
 
29
+ The package owns the ESLint, Prettier, and Knip toolchain. Consuming repos should not install `eslint`, `prettier`, or `knip` directly unless they intentionally need a different version from the shared Ankhorage toolchain.
30
+
25
31
  ## Usage
26
32
 
33
+ ### Scripts
34
+
35
+ Use the devtools-owned binaries in package scripts:
36
+
37
+ ```json
38
+ {
39
+ "scripts": {
40
+ "lint": "ankhorage-eslint . --max-warnings=0",
41
+ "lint:fix": "ankhorage-eslint . --fix --max-warnings=0",
42
+ "format": "ankhorage-prettier --write .",
43
+ "format:check": "ankhorage-prettier --check .",
44
+ "knip": "ankhorage-knip"
45
+ }
46
+ }
47
+ ```
48
+
27
49
  ### ESLint
28
50
 
51
+ Create an `eslint.config.js` file:
52
+
29
53
  ```js
30
- import config from '@ankhorage/devtools/eslint';
54
+ import { createConfig } from '@ankhorage/devtools/eslint';
31
55
 
32
- export default config();
56
+ export default createConfig({
57
+ files: ['src/**/*.{ts,tsx}'],
58
+ project: ['./tsconfig.json'],
59
+ tsconfigRootDir: import.meta.dirname,
60
+ });
33
61
  ```
34
62
 
35
63
  ### Prettier
36
64
 
37
- ```json
38
- {
39
- "extends": "@ankhorage/devtools/prettier"
40
- }
65
+ Create a Prettier config file:
66
+
67
+ ```js
68
+ export { default } from '@ankhorage/devtools/prettier';
69
+ ```
70
+
71
+ CommonJS repos can use:
72
+
73
+ ```js
74
+ module.exports = require('@ankhorage/devtools/prettier');
75
+ ```
76
+
77
+ ### Knip
78
+
79
+ Create a repo-local `knip.config.ts` file:
80
+
81
+ ```ts
82
+ import { createKnipConfig } from '@ankhorage/devtools/knip';
83
+
84
+ export default createKnipConfig();
85
+ ```
86
+
87
+ Repos can add narrow repo-specific patterns when needed:
88
+
89
+ ```ts
90
+ import { createKnipConfig } from '@ankhorage/devtools/knip';
91
+
92
+ export default createKnipConfig({
93
+ entry: ['scripts/release.ts'],
94
+ project: ['scripts/**/*.ts'],
95
+ ignore: ['fixtures/**'],
96
+ ignoreBinaries: ['custom-tool'],
97
+ ignoreFiles: ['examples/fixture.ts'],
98
+ });
99
+ ```
100
+
101
+ The shared config intentionally keeps defaults narrow so Knip can use its own zero-config package discovery. Prefer explicit `entry`, `project`, `ignoreBinaries`, `ignoreDependencies`, or `ignoreFiles` over broad ignores.
102
+
103
+ ### CI
104
+
105
+ Run the scripts in CI after dependencies are installed:
106
+
107
+ ```yaml
108
+ - name: Run lint
109
+ run: bun run lint
110
+
111
+ - name: Run format check
112
+ run: bun run format:check
113
+
114
+ - name: Run Knip
115
+ run: bun run knip
116
+ ```
117
+
118
+ For workflows that support optional scripts, use the same guard style as the other devtools checks:
119
+
120
+ ```yaml
121
+ - name: Run Knip
122
+ run: |
123
+ if node -e "const p=require('./package.json'); process.exit(p.scripts?.knip ? 0 : 1)"; then
124
+ bun run knip
125
+ else
126
+ echo "No knip script found; skipping."
127
+ fi
41
128
  ```
42
129
 
43
130
  ## Use Cases
@@ -45,10 +132,11 @@ export default config();
45
132
  - Monorepos with shared standards
46
133
  - Teams that want strict, predictable linting
47
134
  - Projects avoiding duplicated config
135
+ - Repos that need consistent unused-file, unused-export, and dependency checks
48
136
 
49
137
  ## Why this exists
50
138
 
51
- Maintaining ESLint + Prettier configs across multiple repositories leads to:
139
+ Maintaining ESLint, Prettier, and Knip configs across multiple repositories leads to:
52
140
 
53
141
  - duplication
54
142
  - inconsistency
@@ -60,8 +148,9 @@ This package centralizes tooling so all projects stay aligned.
60
148
 
61
149
  Includes:
62
150
 
63
- - ESLint configuration
64
- - Prettier configuration
151
+ - ESLint configuration and binary wrapper
152
+ - Prettier configuration and binary wrapper
153
+ - Knip configuration and binary wrapper
65
154
 
66
155
  Excludes:
67
156
 
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { runPackageBin } from './internal/runPackageBin.js';
3
+ runPackageBin('eslint', 'eslint');
@@ -0,0 +1 @@
1
+ export declare function runPackageBin(packageName: string, binName: string): void;
@@ -0,0 +1,47 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { readFileSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import { dirname, resolve } from 'node:path';
5
+ const require = createRequire(import.meta.url);
6
+ function isRecord(value) {
7
+ return typeof value === 'object' && value !== null;
8
+ }
9
+ function readPackageBinPath(packageName, binName) {
10
+ const packageJsonPath = require.resolve(`${packageName}/package.json`);
11
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
12
+ if (!isRecord(packageJson)) {
13
+ throw new Error(`Package metadata for ${packageName} is not an object.`);
14
+ }
15
+ const { bin } = packageJson;
16
+ let relativeBinPath;
17
+ if (typeof bin === 'string') {
18
+ relativeBinPath = bin;
19
+ }
20
+ else if (isRecord(bin)) {
21
+ const namedBin = bin[binName];
22
+ if (typeof namedBin === 'string') {
23
+ relativeBinPath = namedBin;
24
+ }
25
+ }
26
+ if (relativeBinPath === undefined) {
27
+ throw new Error(`Package ${packageName} does not expose a ${binName} binary.`);
28
+ }
29
+ return resolve(dirname(packageJsonPath), relativeBinPath);
30
+ }
31
+ export function runPackageBin(packageName, binName) {
32
+ const binPath = readPackageBinPath(packageName, binName);
33
+ const child = spawn(process.execPath, [binPath, ...process.argv.slice(2)], {
34
+ stdio: 'inherit',
35
+ });
36
+ child.on('error', (error) => {
37
+ console.error(error);
38
+ process.exit(1);
39
+ });
40
+ child.on('exit', (code, signal) => {
41
+ if (signal !== null) {
42
+ console.error(`${binName} exited with signal ${signal}.`);
43
+ process.exit(1);
44
+ }
45
+ process.exit(code ?? 1);
46
+ });
47
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { runPackageBin } from './internal/runPackageBin.js';
3
+ runPackageBin('knip', 'knip');
package/dist/knip.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { KnipConfig } from 'knip';
2
+ export interface DevtoolsKnipWorkspaceConfigOptions {
3
+ entry?: string[];
4
+ project?: string[];
5
+ ignore?: string[];
6
+ ignoreBinaries?: string[];
7
+ ignoreDependencies?: (string | RegExp)[];
8
+ ignoreFiles?: string[];
9
+ }
10
+ export interface DevtoolsKnipConfigOptions extends DevtoolsKnipWorkspaceConfigOptions {
11
+ workspaces?: Record<string, DevtoolsKnipWorkspaceConfigOptions>;
12
+ }
13
+ export declare function createKnipConfig(options?: DevtoolsKnipConfigOptions): KnipConfig;
package/dist/knip.js ADDED
@@ -0,0 +1,13 @@
1
+ export function createKnipConfig(options = {}) {
2
+ return {
3
+ ...(options.entry === undefined ? {} : { entry: options.entry }),
4
+ ...(options.project === undefined ? {} : { project: options.project }),
5
+ ...(options.ignore === undefined ? {} : { ignore: options.ignore }),
6
+ ...(options.ignoreBinaries === undefined ? {} : { ignoreBinaries: options.ignoreBinaries }),
7
+ ...(options.ignoreDependencies === undefined
8
+ ? {}
9
+ : { ignoreDependencies: options.ignoreDependencies }),
10
+ ...(options.ignoreFiles === undefined ? {} : { ignoreFiles: options.ignoreFiles }),
11
+ ...(options.workspaces === undefined ? {} : { workspaces: options.workspaces }),
12
+ };
13
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { runPackageBin } from './internal/runPackageBin.js';
3
+ runPackageBin('prettier', 'prettier');
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type tseslint from 'typescript-eslint';
2
- export type FlatConfig = ReturnType<typeof tseslint.config>;
2
+ type FlatConfig = ReturnType<typeof tseslint.config>;
3
3
  export type FlatConfigItem = FlatConfig[number];
4
- export interface RestrictedImport {
4
+ interface RestrictedImport {
5
5
  name: string;
6
6
  message: string;
7
7
  }
@@ -15,3 +15,4 @@ export interface DevtoolsConfigOptions {
15
15
  overrides?: FlatConfigItem[];
16
16
  includePrettier?: boolean;
17
17
  }
18
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/devtools",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Shared Lint and Format Configuration for Ankhorage",
5
5
  "homepage": "https://github.com/ankhorage/devtools#readme",
6
6
  "bugs": {
@@ -14,11 +14,30 @@
14
14
  "publishConfig": {
15
15
  "access": "public"
16
16
  },
17
+ "keywords": [
18
+ "typescript",
19
+ "eslint",
20
+ "prettier",
21
+ "knip",
22
+ "linting",
23
+ "formatting",
24
+ "static-analysis",
25
+ "developer-tools"
26
+ ],
27
+ "bin": {
28
+ "ankhorage-eslint": "./dist/eslint-cli.js",
29
+ "ankhorage-knip": "./dist/knip-cli.js",
30
+ "ankhorage-prettier": "./dist/prettier-cli.js"
31
+ },
17
32
  "exports": {
18
33
  "./eslint": {
19
34
  "types": "./dist/eslint.d.ts",
20
35
  "import": "./dist/eslint.js"
21
36
  },
37
+ "./knip": {
38
+ "types": "./dist/knip.d.ts",
39
+ "import": "./dist/knip.js"
40
+ },
22
41
  "./prettier": {
23
42
  "require": "./dist/prettier.cjs"
24
43
  }
@@ -31,7 +50,9 @@
31
50
  ],
32
51
  "scripts": {
33
52
  "build": "tsc && cp src/prettier.cjs dist/prettier.cjs",
53
+ "knip": "knip",
34
54
  "lint": "eslint .",
55
+ "lint:fix": "eslint . --fix --max-warnings=0",
35
56
  "format": "prettier --write .",
36
57
  "format:check": "prettier --check .",
37
58
  "test": "bun test",
@@ -45,6 +66,7 @@
45
66
  "eslint-plugin-prettier": "^5.5.5",
46
67
  "eslint-plugin-simple-import-sort": "^12.1.1",
47
68
  "eslint-plugin-unused-imports": "^4.4.1",
69
+ "knip": "^6.12.2",
48
70
  "prettier": "^3.8.1",
49
71
  "typescript-eslint": "^8.24.0"
50
72
  },