@ankhorage/devtools 1.2.1 → 1.3.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 +146 -99
- package/dist/cli/commands.d.ts +2 -2
- package/dist/cli/commands.js +31 -75
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +19 -0
- package/dist/cli/runRepositoryCommand.js +40 -27
- package/dist/internal/readmeDocs.js +10 -0
- package/dist/tools/eslint/index.d.ts +1 -1
- package/dist/tools/eslint/index.js +151 -66
- package/dist/tools/eslint/managed.d.ts +10 -0
- package/dist/tools/eslint/managed.js +44 -0
- package/dist/tools/eslint/profile.d.ts +4 -0
- package/dist/tools/eslint/profile.js +81 -0
- package/dist/tools/eslint/types.d.ts +5 -1
- package/dist/tools/knip/index.d.ts +13 -0
- package/dist/tools/knip/managed.d.ts +5 -0
- package/dist/tools/knip/managed.js +11 -0
- package/dist/tools/package/index.d.ts +8 -0
- package/dist/tools/package/index.js +152 -0
- package/dist/tools/prettier/managed.d.ts +6 -0
- package/dist/tools/prettier/managed.js +34 -0
- package/dist/tools/shared/managedFiles.d.ts +6 -1
- package/dist/tools/shared/managedFiles.js +44 -33
- package/package.json +17 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Shared development tools and repository standards for Ankhorage TypeScript proje
|
|
|
4
4
|
|
|
5
5
|
## What it owns
|
|
6
6
|
|
|
7
|
-
`@ankhorage/devtools` is the single source of truth for these
|
|
7
|
+
`@ankhorage/devtools` is the single source of truth for these separate concerns:
|
|
8
8
|
|
|
9
9
|
```text
|
|
10
10
|
src/
|
|
@@ -13,25 +13,39 @@ src/
|
|
|
13
13
|
├── eslint/
|
|
14
14
|
├── prettier/
|
|
15
15
|
├── knip/
|
|
16
|
+
├── package/
|
|
16
17
|
├── workflows/
|
|
17
18
|
└── vscode/
|
|
18
19
|
```
|
|
19
20
|
|
|
20
|
-
- `eslint`: shared flat ESLint configuration and the bundled ESLint runner
|
|
21
|
+
- `eslint`: shared flat ESLint configuration, automatic project profiles, and the bundled ESLint runner
|
|
21
22
|
- `prettier`: shared Prettier configuration and the bundled Prettier runner
|
|
22
|
-
- `knip`: shared Knip configuration and the bundled Knip runner
|
|
23
|
+
- `knip`: shared Knip configuration helpers and the bundled Knip runner
|
|
24
|
+
- `package`: merge-aware synchronization of the shared `package.json` tooling contract
|
|
23
25
|
- `workflows`: canonical `.github/workflows/ci.yml` and `release.yml`
|
|
24
26
|
- `vscode`: canonical `.vscode/settings.json` and `extensions.json`
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
The package owns the supported ESLint, TypeScript ESLint, Prettier, Knip, security, React, React Hooks, React Native, import/sort, unused-import, and formatting-plugin versions used by consuming repositories.
|
|
27
29
|
|
|
28
|
-
##
|
|
30
|
+
## Bootstrap
|
|
31
|
+
|
|
32
|
+
For a repository that does not yet depend on the shared toolchain:
|
|
29
33
|
|
|
30
34
|
```bash
|
|
31
35
|
bun add -D @ankhorage/devtools
|
|
36
|
+
bunx @ankhorage/ankh devtools sync .
|
|
37
|
+
bun install
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
After the first install, the normal workflow is:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
ankh devtools sync
|
|
32
44
|
```
|
|
33
45
|
|
|
34
|
-
The
|
|
46
|
+
The target path is optional and defaults to the current working directory.
|
|
47
|
+
|
|
48
|
+
Synchronization ensures `@ankhorage/devtools` is declared using the version of the provider performing the sync, installs the standard package scripts, and removes direct devDependencies for tools/plugins owned by devtools. Unrelated package metadata, dependencies, and scripts are preserved.
|
|
35
49
|
|
|
36
50
|
## Ankh provider
|
|
37
51
|
|
|
@@ -42,6 +56,14 @@ The package is discovered under the `devtools` category and exposes these capabi
|
|
|
42
56
|
- `devtools.knip`
|
|
43
57
|
- `devtools.sync`
|
|
44
58
|
- `devtools.status`
|
|
59
|
+
- `devtools.eslint.sync`
|
|
60
|
+
- `devtools.eslint.status`
|
|
61
|
+
- `devtools.prettier.sync`
|
|
62
|
+
- `devtools.prettier.status`
|
|
63
|
+
- `devtools.knip.sync`
|
|
64
|
+
- `devtools.knip.status`
|
|
65
|
+
- `devtools.package.sync`
|
|
66
|
+
- `devtools.package.status`
|
|
45
67
|
- `devtools.workflows.sync`
|
|
46
68
|
- `devtools.workflows.status`
|
|
47
69
|
- `devtools.vscode.sync`
|
|
@@ -61,13 +83,13 @@ ankh devtools format -- --check .
|
|
|
61
83
|
ankh devtools knip -- --production
|
|
62
84
|
```
|
|
63
85
|
|
|
64
|
-
These
|
|
86
|
+
These delegate to the same bundled tools as the package binaries:
|
|
65
87
|
|
|
66
88
|
- `ankh devtools lint` → `ankhorage-eslint`
|
|
67
89
|
- `ankh devtools format` → `ankhorage-prettier`
|
|
68
90
|
- `ankh devtools knip` → `ankhorage-knip`
|
|
69
91
|
|
|
70
|
-
|
|
92
|
+
The synchronized package scripts are:
|
|
71
93
|
|
|
72
94
|
```json
|
|
73
95
|
{
|
|
@@ -83,159 +105,184 @@ Recommended package scripts:
|
|
|
83
105
|
|
|
84
106
|
## Repository synchronization
|
|
85
107
|
|
|
86
|
-
|
|
108
|
+
Synchronize or inspect every managed concern:
|
|
87
109
|
|
|
88
110
|
```bash
|
|
89
111
|
ankh devtools sync .
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
The target path is optional and defaults to the current working directory:
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
ankh devtools sync
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Report drift without changing files
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
112
|
ankh devtools status .
|
|
102
113
|
```
|
|
103
114
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Example output:
|
|
107
|
-
|
|
108
|
-
```text
|
|
109
|
-
✓ .github/workflows/ci.yml
|
|
110
|
-
✗ .github/workflows/release.yml outdated
|
|
111
|
-
+ .vscode/settings.json missing
|
|
112
|
-
✓ .vscode/extensions.json
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Synchronize one concern
|
|
115
|
+
Synchronize one concern:
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
+
ankh devtools eslint sync .
|
|
119
|
+
ankh devtools prettier sync .
|
|
120
|
+
ankh devtools knip sync .
|
|
121
|
+
ankh devtools package sync .
|
|
118
122
|
ankh devtools workflows sync .
|
|
119
123
|
ankh devtools vscode sync .
|
|
120
124
|
```
|
|
121
125
|
|
|
122
|
-
|
|
126
|
+
Report one concern:
|
|
123
127
|
|
|
124
128
|
```bash
|
|
129
|
+
ankh devtools eslint status .
|
|
130
|
+
ankh devtools prettier status .
|
|
131
|
+
ankh devtools knip status .
|
|
132
|
+
ankh devtools package status .
|
|
125
133
|
ankh devtools workflows status .
|
|
126
134
|
ankh devtools vscode status .
|
|
127
135
|
```
|
|
128
136
|
|
|
129
|
-
|
|
137
|
+
Preview synchronization without writing:
|
|
130
138
|
|
|
131
139
|
```bash
|
|
132
140
|
ankh devtools sync . --dry-run
|
|
133
|
-
ankh devtools
|
|
134
|
-
ankh devtools
|
|
141
|
+
ankh devtools eslint sync . --dry-run
|
|
142
|
+
ankh devtools package sync . --dry-run
|
|
135
143
|
```
|
|
136
144
|
|
|
137
|
-
A dry run reports `would create` and `would update` actions without
|
|
145
|
+
A dry run reports `would create` and `would update` actions without mutating files. `status` exits with code `1` when managed state has drifted and `0` when it is current.
|
|
138
146
|
|
|
139
147
|
## Synchronization guarantees
|
|
140
148
|
|
|
141
149
|
Synchronization is deterministic and idempotent:
|
|
142
150
|
|
|
143
|
-
- missing managed
|
|
144
|
-
- outdated
|
|
145
|
-
- current
|
|
146
|
-
- unrelated files are
|
|
147
|
-
-
|
|
148
|
-
- repeating `sync` after a successful run produces only `unchanged` results
|
|
151
|
+
- missing managed artifacts are created
|
|
152
|
+
- outdated centrally owned artifacts are updated
|
|
153
|
+
- current artifacts are left untouched
|
|
154
|
+
- unrelated files and package fields are preserved
|
|
155
|
+
- repeated sync produces only `unchanged` results
|
|
149
156
|
- invalid target paths and write failures return a non-zero exit code
|
|
157
|
+
- create-only repository extension files are never overwritten after creation
|
|
150
158
|
|
|
151
|
-
The canonical files are packaged with `@ankhorage/devtools`; synchronization does not fetch mutable files from GitHub at runtime.
|
|
152
|
-
|
|
153
|
-
## Managed GitHub Actions workflows
|
|
154
|
-
|
|
155
|
-
`workflows` owns exactly:
|
|
156
|
-
|
|
157
|
-
```text
|
|
158
|
-
.github/workflows/ci.yml
|
|
159
|
-
.github/workflows/release.yml
|
|
160
|
-
```
|
|
159
|
+
The canonical workflow and VS Code files are packaged with `@ankhorage/devtools`; synchronization does not fetch mutable files from GitHub at runtime.
|
|
161
160
|
|
|
162
|
-
|
|
161
|
+
## ESLint profiles
|
|
163
162
|
|
|
164
|
-
|
|
165
|
-
- installs the pinned Bun version
|
|
166
|
-
- installs dependencies with `bun install --frozen-lockfile`
|
|
167
|
-
- runs `bunx @ankhorage/ankh doctor validate .`
|
|
168
|
-
- conditionally runs build, lint, format check, Knip, tests, and typecheck when scripts exist
|
|
169
|
-
- conditionally runs `changeset:status` for pull requests when the script exists
|
|
163
|
+
`createConfig()` defaults to `profile: 'auto'`.
|
|
170
164
|
|
|
171
|
-
|
|
165
|
+
Automatic detection reads the consuming repository's `package.json` and delegates project trait detection to `@ankhorage/utility/project`. Dependency signals are considered across `dependencies`, `devDependencies`, and `peerDependencies`.
|
|
172
166
|
|
|
173
|
-
|
|
174
|
-
- configures Bun and Node for npm publishing
|
|
175
|
-
- installs dependencies with the frozen lockfile
|
|
176
|
-
- conditionally builds
|
|
177
|
-
- runs the Changesets release PR/publish flow
|
|
178
|
-
- protects release execution with a concurrency group
|
|
179
|
-
- skips cleanly when no Changesets configuration exists
|
|
180
|
-
|
|
181
|
-
## Managed VS Code configuration
|
|
182
|
-
|
|
183
|
-
`vscode` owns exactly:
|
|
167
|
+
Profile precedence is:
|
|
184
168
|
|
|
185
169
|
```text
|
|
186
|
-
|
|
187
|
-
|
|
170
|
+
React Native / Expo
|
|
171
|
+
↓
|
|
172
|
+
react-native
|
|
173
|
+
↓ includes
|
|
174
|
+
react
|
|
175
|
+
↓ includes
|
|
176
|
+
base
|
|
188
177
|
```
|
|
189
178
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
The extension recommendations are limited to the standard Ankhorage workflow:
|
|
193
|
-
|
|
194
|
-
- Bun
|
|
195
|
-
- ESLint
|
|
196
|
-
- Prettier
|
|
197
|
-
- YAML
|
|
198
|
-
- GitHub Actions
|
|
199
|
-
|
|
200
|
-
`launch.json` is intentionally not managed globally. Libraries, CLIs, Expo packages, services, and integration repositories require different debug configurations.
|
|
179
|
+
A React or Next.js project selects `react`. A React Native or Expo project selects `react-native`. Everything else selects `base`.
|
|
201
180
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
Create `eslint.config.mjs`:
|
|
181
|
+
An unusual repository can opt out of automatic selection:
|
|
205
182
|
|
|
206
183
|
```js
|
|
207
184
|
import { createConfig } from '@ankhorage/devtools/eslint';
|
|
208
185
|
|
|
209
186
|
export default createConfig({
|
|
210
|
-
files: ['src/**/*.
|
|
187
|
+
files: ['src/**/*.ts'],
|
|
188
|
+
profile: 'base',
|
|
211
189
|
project: ['./tsconfig.json'],
|
|
212
190
|
tsconfigRootDir: import.meta.dirname,
|
|
213
191
|
});
|
|
214
192
|
```
|
|
215
193
|
|
|
216
|
-
|
|
194
|
+
The base profile enforces the shared TypeScript policy plus:
|
|
195
|
+
|
|
196
|
+
- maximum 50 effective lines per function
|
|
197
|
+
- maximum 300 effective lines per file
|
|
198
|
+
- modified cyclomatic complexity maximum 15
|
|
199
|
+
- security review for dynamic object access
|
|
200
|
+
- rejection of non-literal `require()` calls
|
|
201
|
+
|
|
202
|
+
The React profile adds React and React Hooks correctness rules. The React Native profile composes the React profile and adds the selected React Native style rules.
|
|
203
|
+
|
|
204
|
+
### Managed ESLint setup and local overrides
|
|
217
205
|
|
|
218
|
-
|
|
206
|
+
`ankh devtools eslint sync` centrally owns `eslint.config.mjs` and creates `eslint.local.config.mjs` once.
|
|
207
|
+
|
|
208
|
+
The canonical wrapper uses automatic profile detection and appends repository-owned flat-config entries:
|
|
219
209
|
|
|
220
210
|
```js
|
|
221
|
-
|
|
211
|
+
import { createConfig } from '@ankhorage/devtools/eslint';
|
|
212
|
+
import localConfig from './eslint.local.config.mjs';
|
|
213
|
+
|
|
214
|
+
const localEntries = Array.isArray(localConfig) ? localConfig : [localConfig];
|
|
215
|
+
|
|
216
|
+
export default [
|
|
217
|
+
...createConfig({
|
|
218
|
+
files: ['src/**/*.{ts,tsx}'],
|
|
219
|
+
project: ['./tsconfig.json'],
|
|
220
|
+
tsconfigRootDir: import.meta.dirname,
|
|
221
|
+
}),
|
|
222
|
+
...localEntries,
|
|
223
|
+
];
|
|
222
224
|
```
|
|
223
225
|
|
|
224
|
-
|
|
226
|
+
Use `eslint.local.config.mjs` for narrow repository-specific flat-config overrides, including temporary file-specific migration overrides. On first synchronization, an existing non-canonical `eslint.config.mjs` is preserved as the initial local config before the canonical wrapper is installed. Synchronization never overwrites that local file afterward.
|
|
225
227
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
+
## Prettier
|
|
229
|
+
|
|
230
|
+
`ankh devtools prettier sync` owns `.prettierrc.js` and emits the correct ESM or CommonJS delegate based on the repository's `package.json` module type.
|
|
231
|
+
|
|
232
|
+
The consumer delegates formatting policy to:
|
|
233
|
+
|
|
234
|
+
```text
|
|
235
|
+
@ankhorage/devtools/prettier
|
|
228
236
|
```
|
|
229
237
|
|
|
230
238
|
## Knip
|
|
231
239
|
|
|
240
|
+
`ankh devtools knip sync` bootstraps `knip.config.ts` with:
|
|
241
|
+
|
|
232
242
|
```ts
|
|
233
243
|
import { createKnipConfig } from '@ankhorage/devtools/knip';
|
|
234
244
|
|
|
235
245
|
export default createKnipConfig();
|
|
236
246
|
```
|
|
237
247
|
|
|
238
|
-
|
|
248
|
+
`knip.config.ts` is create-only after bootstrap so repositories can retain narrow local entries, projects, ignores, binaries, dependencies, or switch to `createKnipMonorepoConfig()` without synchronization overwriting those extensions.
|
|
249
|
+
|
|
250
|
+
## Managed package contract
|
|
251
|
+
|
|
252
|
+
`ankh devtools package sync` merge-updates `package.json` rather than replacing it.
|
|
253
|
+
|
|
254
|
+
It owns:
|
|
255
|
+
|
|
256
|
+
- the `@ankhorage/devtools` devDependency version range
|
|
257
|
+
- `lint`
|
|
258
|
+
- `lint:fix`
|
|
259
|
+
- `format`
|
|
260
|
+
- `format:check`
|
|
261
|
+
- `knip`
|
|
262
|
+
|
|
263
|
+
It also removes direct devDependencies for tools and ESLint plugins already provided by `@ankhorage/devtools`. Unrelated scripts, dependencies, metadata, and repository-specific configuration remain unchanged.
|
|
264
|
+
|
|
265
|
+
## Managed GitHub Actions workflows
|
|
266
|
+
|
|
267
|
+
`workflows` owns exactly:
|
|
268
|
+
|
|
269
|
+
```text
|
|
270
|
+
.github/workflows/ci.yml
|
|
271
|
+
.github/workflows/release.yml
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The CI workflow installs the pinned Bun version with the frozen lockfile, builds before repository-provider validation, runs `bunx @ankhorage/ankh doctor validate .`, and conditionally runs lint, formatting, Knip, tests, typecheck, and Changesets checks.
|
|
275
|
+
|
|
276
|
+
## Managed VS Code configuration
|
|
277
|
+
|
|
278
|
+
`vscode` owns exactly:
|
|
279
|
+
|
|
280
|
+
```text
|
|
281
|
+
.vscode/settings.json
|
|
282
|
+
.vscode/extensions.json
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Unknown workflow and VS Code files are never deleted.
|
|
239
286
|
|
|
240
287
|
## Adding another managed concern
|
|
241
288
|
|
|
@@ -243,7 +290,7 @@ A new concern should:
|
|
|
243
290
|
|
|
244
291
|
1. live in its own sibling directory under `src/tools`
|
|
245
292
|
2. define only the files and behavior it owns
|
|
246
|
-
3. expose deterministic status and synchronization
|
|
293
|
+
3. expose deterministic status and synchronization
|
|
247
294
|
4. add provider commands under `ankh devtools`
|
|
248
|
-
5. include
|
|
249
|
-
6. document
|
|
295
|
+
5. include dry-run, status, and idempotence coverage
|
|
296
|
+
6. document its central ownership and repository-owned extension points
|
package/dist/cli/commands.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type DevtoolsToolName = 'format' | 'knip' | 'lint';
|
|
2
|
-
type DevtoolsManagedScope = 'all' | 'vscode' | 'workflows';
|
|
2
|
+
type DevtoolsManagedScope = 'all' | 'eslint' | 'knip' | 'package' | 'prettier' | 'vscode' | 'workflows';
|
|
3
3
|
type DevtoolsManagedOperation = 'status' | 'sync';
|
|
4
|
-
type DevtoolsCapability = 'devtools.format' | 'devtools.knip' | 'devtools.lint' | 'devtools.status' | 'devtools.sync' | 'devtools.vscode.status' | 'devtools.vscode.sync' | 'devtools.workflows.status' | 'devtools.workflows.sync';
|
|
4
|
+
type DevtoolsCapability = 'devtools.format' | 'devtools.knip' | 'devtools.lint' | 'devtools.status' | 'devtools.sync' | 'devtools.eslint.status' | 'devtools.eslint.sync' | 'devtools.knip.status' | 'devtools.knip.sync' | 'devtools.package.status' | 'devtools.package.sync' | 'devtools.prettier.status' | 'devtools.prettier.sync' | 'devtools.vscode.status' | 'devtools.vscode.sync' | 'devtools.workflows.status' | 'devtools.workflows.sync';
|
|
5
5
|
interface DevtoolsCommandBase {
|
|
6
6
|
readonly path: readonly [string, ...string[]];
|
|
7
7
|
readonly capability: DevtoolsCapability;
|
package/dist/cli/commands.js
CHANGED
|
@@ -1,79 +1,21 @@
|
|
|
1
1
|
const DEVTOOLS_COMMANDS = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
kind: 'external',
|
|
22
|
-
toolName: 'knip',
|
|
23
|
-
path: ['knip'],
|
|
24
|
-
capability: 'devtools.knip',
|
|
25
|
-
summary: 'Run the shared Knip toolchain.',
|
|
26
|
-
packageName: 'knip',
|
|
27
|
-
binName: 'knip',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
kind: 'repository',
|
|
31
|
-
path: ['sync'],
|
|
32
|
-
capability: 'devtools.sync',
|
|
33
|
-
summary: 'Synchronize all centrally managed repository files.',
|
|
34
|
-
scope: 'all',
|
|
35
|
-
operation: 'sync',
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
kind: 'repository',
|
|
39
|
-
path: ['status'],
|
|
40
|
-
capability: 'devtools.status',
|
|
41
|
-
summary: 'Report drift for all centrally managed repository files.',
|
|
42
|
-
scope: 'all',
|
|
43
|
-
operation: 'status',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
kind: 'repository',
|
|
47
|
-
path: ['workflows', 'sync'],
|
|
48
|
-
capability: 'devtools.workflows.sync',
|
|
49
|
-
summary: 'Synchronize the canonical GitHub Actions workflows.',
|
|
50
|
-
scope: 'workflows',
|
|
51
|
-
operation: 'sync',
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
kind: 'repository',
|
|
55
|
-
path: ['workflows', 'status'],
|
|
56
|
-
capability: 'devtools.workflows.status',
|
|
57
|
-
summary: 'Report drift for the canonical GitHub Actions workflows.',
|
|
58
|
-
scope: 'workflows',
|
|
59
|
-
operation: 'status',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
kind: 'repository',
|
|
63
|
-
path: ['vscode', 'sync'],
|
|
64
|
-
capability: 'devtools.vscode.sync',
|
|
65
|
-
summary: 'Synchronize the canonical VS Code workspace configuration.',
|
|
66
|
-
scope: 'vscode',
|
|
67
|
-
operation: 'sync',
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
kind: 'repository',
|
|
71
|
-
path: ['vscode', 'status'],
|
|
72
|
-
capability: 'devtools.vscode.status',
|
|
73
|
-
summary: 'Report drift for the canonical VS Code workspace configuration.',
|
|
74
|
-
scope: 'vscode',
|
|
75
|
-
operation: 'status',
|
|
76
|
-
},
|
|
2
|
+
externalCommand('lint', 'devtools.lint', 'Run the shared ESLint toolchain.', 'eslint'),
|
|
3
|
+
externalCommand('format', 'devtools.format', 'Run the shared Prettier toolchain.', 'prettier'),
|
|
4
|
+
externalCommand('knip', 'devtools.knip', 'Run the shared Knip toolchain.', 'knip'),
|
|
5
|
+
repositoryCommand(['sync'], 'devtools.sync', 'Synchronize all managed repository setup.', 'all', 'sync'),
|
|
6
|
+
repositoryCommand(['status'], 'devtools.status', 'Report drift for all managed repository setup.', 'all', 'status'),
|
|
7
|
+
repositoryCommand(['eslint', 'sync'], 'devtools.eslint.sync', 'Synchronize the shared ESLint setup.', 'eslint', 'sync'),
|
|
8
|
+
repositoryCommand(['eslint', 'status'], 'devtools.eslint.status', 'Report drift for the shared ESLint setup.', 'eslint', 'status'),
|
|
9
|
+
repositoryCommand(['prettier', 'sync'], 'devtools.prettier.sync', 'Synchronize the shared Prettier setup.', 'prettier', 'sync'),
|
|
10
|
+
repositoryCommand(['prettier', 'status'], 'devtools.prettier.status', 'Report drift for the shared Prettier setup.', 'prettier', 'status'),
|
|
11
|
+
repositoryCommand(['knip', 'sync'], 'devtools.knip.sync', 'Synchronize the shared Knip setup.', 'knip', 'sync'),
|
|
12
|
+
repositoryCommand(['knip', 'status'], 'devtools.knip.status', 'Report drift for the shared Knip setup.', 'knip', 'status'),
|
|
13
|
+
repositoryCommand(['package', 'sync'], 'devtools.package.sync', 'Synchronize the shared package.json contract.', 'package', 'sync'),
|
|
14
|
+
repositoryCommand(['package', 'status'], 'devtools.package.status', 'Report drift for the shared package.json contract.', 'package', 'status'),
|
|
15
|
+
repositoryCommand(['workflows', 'sync'], 'devtools.workflows.sync', 'Synchronize the canonical GitHub Actions workflows.', 'workflows', 'sync'),
|
|
16
|
+
repositoryCommand(['workflows', 'status'], 'devtools.workflows.status', 'Report drift for the canonical GitHub Actions workflows.', 'workflows', 'status'),
|
|
17
|
+
repositoryCommand(['vscode', 'sync'], 'devtools.vscode.sync', 'Synchronize the canonical VS Code workspace configuration.', 'vscode', 'sync'),
|
|
18
|
+
repositoryCommand(['vscode', 'status'], 'devtools.vscode.status', 'Report drift for the canonical VS Code workspace configuration.', 'vscode', 'status'),
|
|
77
19
|
];
|
|
78
20
|
export function getDevtoolsCommands() {
|
|
79
21
|
return DEVTOOLS_COMMANDS;
|
|
@@ -90,3 +32,17 @@ export function getDevtoolsToolCommand(toolName) {
|
|
|
90
32
|
}
|
|
91
33
|
throw new Error(`Unknown devtools tool command: ${toolName}`);
|
|
92
34
|
}
|
|
35
|
+
function externalCommand(toolName, capability, summary, packageName) {
|
|
36
|
+
return {
|
|
37
|
+
kind: 'external',
|
|
38
|
+
toolName,
|
|
39
|
+
path: [toolName],
|
|
40
|
+
capability,
|
|
41
|
+
summary,
|
|
42
|
+
packageName,
|
|
43
|
+
binName: packageName,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function repositoryCommand(path, capability, summary, scope, operation) {
|
|
47
|
+
return { kind: 'repository', path, capability, summary, scope, operation };
|
|
48
|
+
}
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ declare const provider: {
|
|
|
2
2
|
id: string;
|
|
3
3
|
category: string;
|
|
4
4
|
version: string;
|
|
5
|
-
capabilities: ("devtools.format" | "devtools.knip" | "devtools.lint" | "devtools.status" | "devtools.sync" | "devtools.vscode.status" | "devtools.vscode.sync" | "devtools.workflows.status" | "devtools.workflows.sync")[];
|
|
5
|
+
capabilities: ("devtools.format" | "devtools.knip" | "devtools.lint" | "devtools.status" | "devtools.sync" | "devtools.eslint.status" | "devtools.eslint.sync" | "devtools.knip.status" | "devtools.knip.sync" | "devtools.package.status" | "devtools.package.sync" | "devtools.prettier.status" | "devtools.prettier.sync" | "devtools.vscode.status" | "devtools.vscode.sync" | "devtools.workflows.status" | "devtools.workflows.sync")[];
|
|
6
6
|
commands: {
|
|
7
7
|
path: [string, ...string[]];
|
|
8
|
-
capability: "devtools.format" | "devtools.knip" | "devtools.lint" | "devtools.status" | "devtools.sync" | "devtools.vscode.status" | "devtools.vscode.sync" | "devtools.workflows.status" | "devtools.workflows.sync";
|
|
8
|
+
capability: "devtools.format" | "devtools.knip" | "devtools.lint" | "devtools.status" | "devtools.sync" | "devtools.eslint.status" | "devtools.eslint.sync" | "devtools.knip.status" | "devtools.knip.sync" | "devtools.package.status" | "devtools.package.sync" | "devtools.prettier.status" | "devtools.prettier.sync" | "devtools.vscode.status" | "devtools.vscode.sync" | "devtools.workflows.status" | "devtools.workflows.sync";
|
|
9
9
|
summary: string;
|
|
10
10
|
}[];
|
|
11
11
|
handlers: {
|
package/dist/cli/index.js
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/***
|
|
2
|
+
* Run and synchronize the shared development toolchain through the Ankh CLI.
|
|
3
|
+
*
|
|
4
|
+
* `ankh devtools lint`, `ankh devtools format`, and `ankh devtools knip` execute the bundled
|
|
5
|
+
* ESLint, Prettier, and Knip versions. Repository synchronization is available through
|
|
6
|
+
* `ankh devtools sync` and `ankh devtools status`, with focused `eslint`, `prettier`, `knip`,
|
|
7
|
+
* `package`, `workflows`, and `vscode` sync/status subcommands.
|
|
8
|
+
*
|
|
9
|
+
* Sync commands accept an optional target directory and `--dry-run`. Aggregate sync is
|
|
10
|
+
* deterministic and idempotent: canonical managed files are created or updated, create-only
|
|
11
|
+
* local extension files remain repository-owned, and package metadata is merge-updated without
|
|
12
|
+
* replacing unrelated fields.
|
|
13
|
+
*
|
|
14
|
+
* Fresh repositories can bootstrap the standard setup with `ankh devtools sync .` after adding
|
|
15
|
+
* `@ankhorage/devtools`. Existing ESLint configuration is preserved during first migration as a
|
|
16
|
+
* local extension before the canonical auto-detecting wrapper is installed.
|
|
17
|
+
*
|
|
18
|
+
* @readme
|
|
19
|
+
*/
|
|
1
20
|
import { readFileSync } from 'node:fs';
|
|
2
21
|
import { getDevtoolsCommands } from './commands.js';
|
|
3
22
|
import { runProviderCommand } from './runProviderCommand.js';
|
|
@@ -1,30 +1,18 @@
|
|
|
1
|
+
import { eslintManagedFiles } from '../tools/eslint/managed.js';
|
|
2
|
+
import { knipManagedFiles } from '../tools/knip/managed.js';
|
|
3
|
+
import { inspectPackageManifest, readCurrentDevtoolsVersion, syncPackageManifest, } from '../tools/package/index.js';
|
|
4
|
+
import { prettierManagedFiles } from '../tools/prettier/managed.js';
|
|
1
5
|
import { inspectManagedFiles, resolveManagedTargetDirectory, syncManagedFiles, } from '../tools/shared/managedFiles.js';
|
|
2
6
|
import { vscodeManagedFiles } from '../tools/vscode/index.js';
|
|
3
7
|
import { workflowManagedFiles } from '../tools/workflows/index.js';
|
|
4
8
|
export async function runRepositoryCommand(command, argv, context) {
|
|
5
|
-
let parsedArguments;
|
|
6
9
|
try {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
const targetDirectory = await resolveManagedTargetDirectory(context.cwd, parsedArguments.targetPath);
|
|
15
|
-
const definitions = getManagedFiles(command.scope);
|
|
16
|
-
if (command.operation === 'status') {
|
|
17
|
-
const statuses = await inspectManagedFiles(targetDirectory, definitions);
|
|
18
|
-
writeStatusOutput(statuses, context);
|
|
19
|
-
return {
|
|
20
|
-
exitCode: statuses.some((status) => status.state !== 'current') ? 1 : 0,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
const results = await syncManagedFiles(targetDirectory, definitions, {
|
|
24
|
-
dryRun: parsedArguments.dryRun,
|
|
25
|
-
});
|
|
26
|
-
writeSyncOutput(results, context);
|
|
27
|
-
return { exitCode: 0 };
|
|
10
|
+
const parsed = parseRepositoryArguments(argv, command.operation === 'sync');
|
|
11
|
+
const targetDirectory = await resolveManagedTargetDirectory(context.cwd, parsed.targetPath);
|
|
12
|
+
const devtoolsVersion = readCurrentDevtoolsVersion();
|
|
13
|
+
return command.operation === 'status'
|
|
14
|
+
? await runStatus(command.scope, targetDirectory, devtoolsVersion, context)
|
|
15
|
+
: await runSync(command.scope, targetDirectory, devtoolsVersion, parsed.dryRun, context);
|
|
28
16
|
}
|
|
29
17
|
catch (error) {
|
|
30
18
|
context.writeStderr(`${getErrorMessage(error)}\n`);
|
|
@@ -52,14 +40,39 @@ export function parseRepositoryArguments(argv, allowDryRun) {
|
|
|
52
40
|
}
|
|
53
41
|
return { dryRun, targetPath };
|
|
54
42
|
}
|
|
43
|
+
async function runStatus(scope, targetDirectory, devtoolsVersion, context) {
|
|
44
|
+
const statuses = [];
|
|
45
|
+
if (scope === 'all' || scope === 'package') {
|
|
46
|
+
statuses.push(await inspectPackageManifest(targetDirectory, devtoolsVersion));
|
|
47
|
+
}
|
|
48
|
+
statuses.push(...(await inspectManagedFiles(targetDirectory, getManagedFiles(scope))));
|
|
49
|
+
writeStatusOutput(statuses, context);
|
|
50
|
+
return { exitCode: statuses.some((status) => status.state !== 'current') ? 1 : 0 };
|
|
51
|
+
}
|
|
52
|
+
async function runSync(scope, targetDirectory, devtoolsVersion, dryRun, context) {
|
|
53
|
+
const results = [];
|
|
54
|
+
if (scope === 'all' || scope === 'package') {
|
|
55
|
+
results.push(await syncPackageManifest(targetDirectory, devtoolsVersion, { dryRun }));
|
|
56
|
+
}
|
|
57
|
+
results.push(...(await syncManagedFiles(targetDirectory, getManagedFiles(scope), { dryRun })));
|
|
58
|
+
writeSyncOutput(results, context);
|
|
59
|
+
return { exitCode: 0 };
|
|
60
|
+
}
|
|
55
61
|
function getManagedFiles(scope) {
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
const definitionsByScope = {
|
|
63
|
+
eslint: eslintManagedFiles,
|
|
64
|
+
knip: knipManagedFiles,
|
|
65
|
+
prettier: prettierManagedFiles,
|
|
66
|
+
vscode: vscodeManagedFiles,
|
|
67
|
+
workflows: workflowManagedFiles,
|
|
68
|
+
};
|
|
69
|
+
if (scope === 'all') {
|
|
70
|
+
return Object.values(definitionsByScope).flat();
|
|
58
71
|
}
|
|
59
|
-
if (scope === '
|
|
60
|
-
return
|
|
72
|
+
if (scope === 'package') {
|
|
73
|
+
return [];
|
|
61
74
|
}
|
|
62
|
-
return [
|
|
75
|
+
return definitionsByScope[scope];
|
|
63
76
|
}
|
|
64
77
|
function writeStatusOutput(statuses, context) {
|
|
65
78
|
for (const status of statuses) {
|
|
@@ -4,11 +4,21 @@ const REQUIRED_README_SNIPPETS = [
|
|
|
4
4
|
'ankh devtools knip',
|
|
5
5
|
'ankh devtools sync',
|
|
6
6
|
'ankh devtools status',
|
|
7
|
+
'ankh devtools eslint sync',
|
|
8
|
+
'ankh devtools prettier sync',
|
|
9
|
+
'ankh devtools knip sync',
|
|
10
|
+
'ankh devtools package sync',
|
|
7
11
|
'ankh devtools workflows sync',
|
|
8
12
|
'ankh devtools vscode sync',
|
|
13
|
+
'devtools.eslint.sync',
|
|
14
|
+
'devtools.prettier.sync',
|
|
15
|
+
'devtools.knip.sync',
|
|
16
|
+
'devtools.package.sync',
|
|
9
17
|
'devtools.workflows.sync',
|
|
10
18
|
'devtools.vscode.sync',
|
|
11
19
|
'--dry-run',
|
|
20
|
+
"profile: 'auto'",
|
|
21
|
+
'@ankhorage/utility/project',
|
|
12
22
|
];
|
|
13
23
|
export function getReadmeDocumentationErrors(readmeContents) {
|
|
14
24
|
return REQUIRED_README_SNIPPETS.flatMap((snippet) => readmeContents.includes(snippet)
|
|
@@ -6,4 +6,4 @@ export declare const defaultRestrictedImports: readonly [{
|
|
|
6
6
|
readonly message: "Forbidden in Ankhorage packages. Use '@ankhorage/react-native-reanimated-dnd-web' directly.";
|
|
7
7
|
}];
|
|
8
8
|
export declare function createConfig(options: DevtoolsConfigOptions): ReturnType<typeof tseslint.config>;
|
|
9
|
-
export type { DevtoolsConfigOptions, FlatConfigItem } from './types.js';
|
|
9
|
+
export type { DevtoolsConfigOptions, DevtoolsEslintProfile, FlatConfigItem, ResolvedDevtoolsEslintProfile, RestrictedImport, } from './types.js';
|