@ankhorage/devtools 1.1.2 → 1.2.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.
Files changed (48) hide show
  1. package/README.md +165 -138
  2. package/dist/cli/bin/eslint.js +4 -0
  3. package/dist/cli/bin/knip.js +4 -0
  4. package/dist/cli/bin/prettier.js +4 -0
  5. package/dist/cli/commands.d.ts +25 -0
  6. package/dist/cli/commands.js +92 -0
  7. package/dist/cli/index.d.ts +5 -5
  8. package/dist/cli/index.js +3 -4
  9. package/dist/cli/runExternalTool.d.ts +31 -0
  10. package/dist/{internal/runDevtoolsCommand.js → cli/runExternalTool.js} +3 -3
  11. package/dist/cli/runProviderCommand.d.ts +8 -0
  12. package/dist/cli/runProviderCommand.js +11 -0
  13. package/dist/cli/runRepositoryCommand.d.ts +16 -0
  14. package/dist/cli/runRepositoryCommand.js +108 -0
  15. package/dist/cli/runStandaloneTool.d.ts +3 -0
  16. package/dist/cli/runStandaloneTool.js +5 -0
  17. package/dist/internal/readmeDocs.js +7 -7
  18. package/dist/{eslint.d.ts → tools/eslint/index.d.ts} +1 -0
  19. package/dist/tools/eslint/types.d.ts +18 -0
  20. package/dist/tools/knip/index.d.ts +20 -0
  21. package/dist/tools/shared/managedFiles.d.ts +20 -0
  22. package/dist/tools/shared/managedFiles.js +84 -0
  23. package/dist/tools/vscode/files/extensions.json +9 -0
  24. package/dist/tools/vscode/files/settings.json +15 -0
  25. package/dist/tools/vscode/index.d.ts +7 -0
  26. package/dist/tools/vscode/index.js +10 -0
  27. package/dist/tools/workflows/files/ci.yml +88 -0
  28. package/dist/tools/workflows/files/release.yml +65 -0
  29. package/dist/tools/workflows/index.d.ts +7 -0
  30. package/dist/tools/workflows/index.js +10 -0
  31. package/package.json +21 -15
  32. package/dist/eslint-cli.js +0 -4
  33. package/dist/internal/devtoolsCommands.d.ts +0 -11
  34. package/dist/internal/devtoolsCommands.js +0 -39
  35. package/dist/internal/runDevtoolsCommand.d.ts +0 -31
  36. package/dist/internal/runStandaloneDevtoolsCommand.d.ts +0 -3
  37. package/dist/internal/runStandaloneDevtoolsCommand.js +0 -5
  38. package/dist/knip-cli.js +0 -4
  39. package/dist/knip.d.ts +0 -20
  40. package/dist/prettier-cli.js +0 -4
  41. package/dist/types.d.ts +0 -18
  42. /package/dist/{eslint-cli.d.ts → cli/bin/eslint.d.ts} +0 -0
  43. /package/dist/{knip-cli.d.ts → cli/bin/knip.d.ts} +0 -0
  44. /package/dist/{prettier-cli.d.ts → cli/bin/prettier.d.ts} +0 -0
  45. /package/dist/{eslint.js → tools/eslint/index.js} +0 -0
  46. /package/dist/{types.js → tools/eslint/types.js} +0 -0
  47. /package/dist/{knip.js → tools/knip/index.js} +0 -0
  48. /package/dist/{prettier.cjs → tools/prettier/index.cjs} +0 -0
@@ -0,0 +1,108 @@
1
+ import { inspectManagedFiles, resolveManagedTargetDirectory, syncManagedFiles, } from '../tools/shared/managedFiles.js';
2
+ import { vscodeManagedFiles } from '../tools/vscode/index.js';
3
+ import { workflowManagedFiles } from '../tools/workflows/index.js';
4
+ export async function runRepositoryCommand(command, argv, context) {
5
+ let parsedArguments;
6
+ try {
7
+ parsedArguments = parseRepositoryArguments(argv, command.operation === 'sync');
8
+ }
9
+ catch (error) {
10
+ context.writeStderr(`${getErrorMessage(error)}\n`);
11
+ return { exitCode: 1 };
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 };
28
+ }
29
+ catch (error) {
30
+ context.writeStderr(`${getErrorMessage(error)}\n`);
31
+ return { exitCode: 1 };
32
+ }
33
+ }
34
+ export function parseRepositoryArguments(argv, allowDryRun) {
35
+ let dryRun = false;
36
+ let targetPath;
37
+ for (const argument of argv) {
38
+ if (argument === '--dry-run') {
39
+ if (!allowDryRun) {
40
+ throw new Error('--dry-run is only valid for sync commands.');
41
+ }
42
+ dryRun = true;
43
+ continue;
44
+ }
45
+ if (argument.startsWith('-')) {
46
+ throw new Error(`Unknown option: ${argument}`);
47
+ }
48
+ if (targetPath !== undefined) {
49
+ throw new Error('Only one target path may be provided.');
50
+ }
51
+ targetPath = argument;
52
+ }
53
+ return { dryRun, targetPath };
54
+ }
55
+ function getManagedFiles(scope) {
56
+ if (scope === 'workflows') {
57
+ return workflowManagedFiles;
58
+ }
59
+ if (scope === 'vscode') {
60
+ return vscodeManagedFiles;
61
+ }
62
+ return [...workflowManagedFiles, ...vscodeManagedFiles];
63
+ }
64
+ function writeStatusOutput(statuses, context) {
65
+ for (const status of statuses) {
66
+ if (status.state === 'current') {
67
+ context.writeStdout(`✓ ${status.relativePath}\n`);
68
+ }
69
+ else if (status.state === 'missing') {
70
+ context.writeStdout(`+ ${status.relativePath} missing\n`);
71
+ }
72
+ else {
73
+ context.writeStdout(`✗ ${status.relativePath} outdated\n`);
74
+ }
75
+ }
76
+ }
77
+ function writeSyncOutput(results, context) {
78
+ for (const result of results) {
79
+ const prefix = getActionPrefix(result.action);
80
+ context.writeStdout(`${prefix} ${result.relativePath} ${formatAction(result.action)}\n`);
81
+ }
82
+ }
83
+ function getActionPrefix(action) {
84
+ if (action === 'unchanged') {
85
+ return '✓';
86
+ }
87
+ if (action === 'created' || action === 'would-create') {
88
+ return '+';
89
+ }
90
+ return '↻';
91
+ }
92
+ function formatAction(action) {
93
+ switch (action) {
94
+ case 'created':
95
+ return 'created';
96
+ case 'updated':
97
+ return 'updated';
98
+ case 'unchanged':
99
+ return 'unchanged';
100
+ case 'would-create':
101
+ return 'would create';
102
+ case 'would-update':
103
+ return 'would update';
104
+ }
105
+ }
106
+ function getErrorMessage(error) {
107
+ return error instanceof Error ? error.message : String(error);
108
+ }
@@ -0,0 +1,3 @@
1
+ import { type DevtoolsToolName } from './commands.js';
2
+ import { type DevtoolsRunResult } from './runExternalTool.js';
3
+ export declare function runStandaloneTool(toolName: DevtoolsToolName, argv: readonly string[]): Promise<DevtoolsRunResult>;
@@ -0,0 +1,5 @@
1
+ import { getDevtoolsToolCommand } from './commands.js';
2
+ import { runExternalTool } from './runExternalTool.js';
3
+ export async function runStandaloneTool(toolName, argv) {
4
+ return await runExternalTool(getDevtoolsToolCommand(toolName), argv);
5
+ }
@@ -2,13 +2,13 @@ const REQUIRED_README_SNIPPETS = [
2
2
  'ankh devtools lint',
3
3
  'ankh devtools format',
4
4
  'ankh devtools knip',
5
- 'ankhorage-eslint',
6
- 'ankhorage-prettier',
7
- 'ankhorage-knip',
8
- 'devtools',
9
- 'devtools.lint',
10
- 'devtools.format',
11
- 'devtools.knip',
5
+ 'ankh devtools sync',
6
+ 'ankh devtools status',
7
+ 'ankh devtools workflows sync',
8
+ 'ankh devtools vscode sync',
9
+ 'devtools.workflows.sync',
10
+ 'devtools.vscode.sync',
11
+ '--dry-run',
12
12
  ];
13
13
  export function getReadmeDocumentationErrors(readmeContents) {
14
14
  return REQUIRED_README_SNIPPETS.flatMap((snippet) => readmeContents.includes(snippet)
@@ -6,3 +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';
@@ -0,0 +1,18 @@
1
+ import type tseslint from 'typescript-eslint';
2
+ type FlatConfig = ReturnType<typeof tseslint.config>;
3
+ export type FlatConfigItem = FlatConfig[number];
4
+ interface RestrictedImport {
5
+ readonly name: string;
6
+ readonly message: string;
7
+ }
8
+ export interface DevtoolsConfigOptions {
9
+ readonly tsconfigRootDir: string;
10
+ readonly project: string[];
11
+ readonly files: string[];
12
+ readonly allowDefaultProject?: string[];
13
+ readonly additionalIgnores?: string[];
14
+ readonly restrictedImports?: RestrictedImport[];
15
+ readonly overrides?: FlatConfigItem[];
16
+ readonly includePrettier?: boolean;
17
+ }
18
+ export {};
@@ -0,0 +1,20 @@
1
+ import type { KnipConfig } from 'knip';
2
+ export interface DevtoolsKnipWorkspaceConfigOptions {
3
+ readonly entry?: string[];
4
+ readonly project?: string[];
5
+ readonly ignore?: string[];
6
+ readonly ignoreBinaries?: string[];
7
+ readonly ignoreDependencies?: (string | RegExp)[];
8
+ readonly ignoreFiles?: string[];
9
+ }
10
+ export interface DevtoolsKnipConfigOptions extends DevtoolsKnipWorkspaceConfigOptions {
11
+ readonly workspaces?: Record<string, DevtoolsKnipWorkspaceConfigOptions>;
12
+ }
13
+ export interface DevtoolsKnipMonorepoConfigOptions {
14
+ readonly root?: DevtoolsKnipWorkspaceConfigOptions;
15
+ readonly workspaceDefaults?: DevtoolsKnipWorkspaceConfigOptions;
16
+ readonly workspaceGlobs?: string[];
17
+ readonly workspaces?: Record<string, DevtoolsKnipWorkspaceConfigOptions>;
18
+ }
19
+ export declare function createKnipConfig(options?: DevtoolsKnipConfigOptions): KnipConfig;
20
+ export declare function createKnipMonorepoConfig(options?: DevtoolsKnipMonorepoConfigOptions): KnipConfig;
@@ -0,0 +1,20 @@
1
+ export interface ManagedFileDefinition {
2
+ readonly relativePath: string;
3
+ readonly sourceUrl: URL;
4
+ }
5
+ type ManagedFileState = 'current' | 'missing' | 'outdated';
6
+ type ManagedFileSyncAction = 'unchanged' | 'created' | 'updated' | 'would-create' | 'would-update';
7
+ export interface ManagedFileStatus {
8
+ readonly relativePath: string;
9
+ readonly state: ManagedFileState;
10
+ }
11
+ export interface ManagedFileSyncResult {
12
+ readonly relativePath: string;
13
+ readonly action: ManagedFileSyncAction;
14
+ }
15
+ export declare function resolveManagedTargetDirectory(cwd: string, requestedPath: string | undefined): Promise<string>;
16
+ export declare function inspectManagedFiles(targetDirectory: string, definitions: readonly ManagedFileDefinition[]): Promise<readonly ManagedFileStatus[]>;
17
+ export declare function syncManagedFiles(targetDirectory: string, definitions: readonly ManagedFileDefinition[], options: {
18
+ readonly dryRun: boolean;
19
+ }): Promise<readonly ManagedFileSyncResult[]>;
20
+ export {};
@@ -0,0 +1,84 @@
1
+ import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
2
+ import { dirname, resolve } from 'node:path';
3
+ export async function resolveManagedTargetDirectory(cwd, requestedPath) {
4
+ const targetDirectory = resolve(cwd, requestedPath ?? '.');
5
+ let targetStats;
6
+ try {
7
+ targetStats = await stat(targetDirectory);
8
+ }
9
+ catch (error) {
10
+ throw new Error(`Target directory does not exist: ${targetDirectory}`, { cause: error });
11
+ }
12
+ if (!targetStats.isDirectory()) {
13
+ throw new Error(`Target path is not a directory: ${targetDirectory}`);
14
+ }
15
+ return targetDirectory;
16
+ }
17
+ export async function inspectManagedFiles(targetDirectory, definitions) {
18
+ return await Promise.all(definitions.map(async (definition) => {
19
+ const canonicalContents = await readCanonicalContents(definition);
20
+ const targetPath = resolve(targetDirectory, definition.relativePath);
21
+ try {
22
+ const targetContents = await readFile(targetPath, 'utf8');
23
+ return {
24
+ relativePath: definition.relativePath,
25
+ state: targetContents === canonicalContents ? 'current' : 'outdated',
26
+ };
27
+ }
28
+ catch (error) {
29
+ if (isMissingFileError(error)) {
30
+ return {
31
+ relativePath: definition.relativePath,
32
+ state: 'missing',
33
+ };
34
+ }
35
+ throw new Error(`Failed to inspect managed file: ${targetPath}`, { cause: error });
36
+ }
37
+ }));
38
+ }
39
+ export async function syncManagedFiles(targetDirectory, definitions, options) {
40
+ const statuses = await inspectManagedFiles(targetDirectory, definitions);
41
+ const definitionsByPath = new Map(definitions.map((definition) => [definition.relativePath, definition]));
42
+ const results = [];
43
+ for (const status of statuses) {
44
+ if (status.state === 'current') {
45
+ results.push({ relativePath: status.relativePath, action: 'unchanged' });
46
+ continue;
47
+ }
48
+ const definition = definitionsByPath.get(status.relativePath);
49
+ if (definition === undefined) {
50
+ throw new Error(`Missing managed file definition for ${status.relativePath}.`);
51
+ }
52
+ if (options.dryRun) {
53
+ results.push({
54
+ relativePath: status.relativePath,
55
+ action: status.state === 'missing' ? 'would-create' : 'would-update',
56
+ });
57
+ continue;
58
+ }
59
+ const targetPath = resolve(targetDirectory, definition.relativePath);
60
+ await mkdir(dirname(targetPath), { recursive: true });
61
+ await writeFile(targetPath, await readCanonicalContents(definition), 'utf8');
62
+ results.push({
63
+ relativePath: status.relativePath,
64
+ action: status.state === 'missing' ? 'created' : 'updated',
65
+ });
66
+ }
67
+ return results;
68
+ }
69
+ async function readCanonicalContents(definition) {
70
+ try {
71
+ return await readFile(definition.sourceUrl, 'utf8');
72
+ }
73
+ catch (error) {
74
+ throw new Error(`Failed to read canonical managed file: ${definition.relativePath}`, {
75
+ cause: error,
76
+ });
77
+ }
78
+ }
79
+ function isMissingFileError(error) {
80
+ return isNodeError(error) && error.code === 'ENOENT';
81
+ }
82
+ function isNodeError(error) {
83
+ return error instanceof Error && 'code' in error;
84
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "recommendations": [
3
+ "oven.bun-vscode",
4
+ "dbaeumer.vscode-eslint",
5
+ "esbenp.prettier-vscode",
6
+ "redhat.vscode-yaml",
7
+ "github.vscode-github-actions"
8
+ ]
9
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
3
+ "editor.formatOnSave": false,
4
+ "editor.codeActionsOnSave": {
5
+ "source.fixAll.eslint": "explicit"
6
+ },
7
+ "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
8
+ "typescript.tsdk": "node_modules/typescript/lib",
9
+ "typescript.enablePromptUseWorkspaceTsdk": true,
10
+ "files.insertFinalNewline": true,
11
+ "files.trimFinalNewlines": true,
12
+ "files.trimTrailingWhitespace": true,
13
+ "editor.tabSize": 2,
14
+ "editor.detectIndentation": false
15
+ }
@@ -0,0 +1,7 @@
1
+ export declare const vscodeManagedFiles: readonly [{
2
+ readonly relativePath: ".vscode/settings.json";
3
+ readonly sourceUrl: URL;
4
+ }, {
5
+ readonly relativePath: ".vscode/extensions.json";
6
+ readonly sourceUrl: URL;
7
+ }];
@@ -0,0 +1,10 @@
1
+ export const vscodeManagedFiles = [
2
+ {
3
+ relativePath: '.vscode/settings.json',
4
+ sourceUrl: new URL('./files/settings.json', import.meta.url),
5
+ },
6
+ {
7
+ relativePath: '.vscode/extensions.json',
8
+ sourceUrl: new URL('./files/extensions.json', import.meta.url),
9
+ },
10
+ ];
@@ -0,0 +1,88 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ validate:
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - name: Checkout repository
18
+ uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 0
21
+
22
+ - name: Setup Bun
23
+ uses: oven-sh/setup-bun@v2
24
+ with:
25
+ bun-version: '1.3.13'
26
+
27
+ - name: Install dependencies
28
+ run: bun install --frozen-lockfile
29
+
30
+ - name: Validate Ankhorage repository
31
+ run: bunx @ankhorage/ankh doctor validate .
32
+
33
+ - name: Run build
34
+ run: |
35
+ if node -e "const p=require('./package.json'); process.exit(p.scripts?.build ? 0 : 1)"; then
36
+ bun run build
37
+ else
38
+ echo "No build script found; skipping."
39
+ fi
40
+
41
+ - name: Run lint
42
+ run: |
43
+ if node -e "const p=require('./package.json'); process.exit(p.scripts?.lint ? 0 : 1)"; then
44
+ bun run lint
45
+ else
46
+ echo "No lint script found; skipping."
47
+ fi
48
+
49
+ - name: Run format check
50
+ run: |
51
+ if node -e "const p=require('./package.json'); process.exit(p.scripts?.['format:check'] ? 0 : 1)"; then
52
+ bun run format:check
53
+ else
54
+ echo "No format:check script found; skipping."
55
+ fi
56
+
57
+ - name: Run Knip
58
+ run: |
59
+ if node -e "const p=require('./package.json'); process.exit(p.scripts?.knip ? 0 : 1)"; then
60
+ bun run knip
61
+ else
62
+ echo "No knip script found; skipping."
63
+ fi
64
+
65
+ - name: Run tests
66
+ run: |
67
+ if node -e "const p=require('./package.json'); process.exit(p.scripts?.test ? 0 : 1)"; then
68
+ bun run test
69
+ else
70
+ echo "No test script found; skipping."
71
+ fi
72
+
73
+ - name: Run typecheck
74
+ run: |
75
+ if node -e "const p=require('./package.json'); process.exit(p.scripts?.typecheck ? 0 : 1)"; then
76
+ bun run typecheck
77
+ else
78
+ echo "No typecheck script found; skipping."
79
+ fi
80
+
81
+ - name: Check changesets
82
+ if: github.event_name == 'pull_request'
83
+ run: |
84
+ if node -e "const p=require('./package.json'); process.exit(p.scripts?.['changeset:status'] ? 0 : 1)"; then
85
+ bun run changeset:status
86
+ else
87
+ echo "No changeset:status script found; skipping."
88
+ fi
@@ -0,0 +1,65 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ contents: write
10
+ pull-requests: write
11
+ id-token: write
12
+
13
+ concurrency:
14
+ group: release-${{ github.ref }}
15
+ cancel-in-progress: false
16
+
17
+ jobs:
18
+ release:
19
+ runs-on: ubuntu-latest
20
+
21
+ steps:
22
+ - name: Checkout repository
23
+ uses: actions/checkout@v4
24
+ with:
25
+ fetch-depth: 0
26
+
27
+ - name: Setup Bun
28
+ uses: oven-sh/setup-bun@v2
29
+ with:
30
+ bun-version: '1.3.13'
31
+
32
+ - name: Setup Node for npm publishing
33
+ uses: actions/setup-node@v4
34
+ with:
35
+ node-version: 24
36
+ registry-url: https://registry.npmjs.org
37
+
38
+ - name: Update npm
39
+ run: npm install -g npm@latest
40
+
41
+ - name: Install dependencies
42
+ run: bun install --frozen-lockfile
43
+
44
+ - name: Build package
45
+ run: |
46
+ if node -e "const p=require('./package.json'); process.exit(p.scripts?.build ? 0 : 1)"; then
47
+ bun run build
48
+ else
49
+ echo "No build script found; skipping."
50
+ fi
51
+
52
+ - name: Create release pull request or publish to npm
53
+ if: hashFiles('.changeset/config.json') != ''
54
+ uses: changesets/action@v1
55
+ with:
56
+ version: bun run version-packages
57
+ publish: bunx changeset publish
58
+ commit: Version Packages
59
+ title: Version Packages
60
+ env:
61
+ GITHUB_TOKEN: ${{ github.token }}
62
+
63
+ - name: Skip release
64
+ if: hashFiles('.changeset/config.json') == ''
65
+ run: echo "No Changesets config found; skipping release."
@@ -0,0 +1,7 @@
1
+ export declare const workflowManagedFiles: readonly [{
2
+ readonly relativePath: ".github/workflows/ci.yml";
3
+ readonly sourceUrl: URL;
4
+ }, {
5
+ readonly relativePath: ".github/workflows/release.yml";
6
+ readonly sourceUrl: URL;
7
+ }];
@@ -0,0 +1,10 @@
1
+ export const workflowManagedFiles = [
2
+ {
3
+ relativePath: '.github/workflows/ci.yml',
4
+ sourceUrl: new URL('./files/ci.yml', import.meta.url),
5
+ },
6
+ {
7
+ relativePath: '.github/workflows/release.yml',
8
+ sourceUrl: new URL('./files/release.yml', import.meta.url),
9
+ },
10
+ ];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/devtools",
3
- "version": "1.1.2",
4
- "description": "Shared Lint and Format Configuration for Ankhorage",
3
+ "version": "1.2.0",
4
+ "description": "Shared development tools and repository standards for Ankhorage",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/ankhorage/devtools#readme",
7
7
  "bugs": {
@@ -21,7 +21,13 @@
21
21
  "capabilities": [
22
22
  "devtools.lint",
23
23
  "devtools.format",
24
- "devtools.knip"
24
+ "devtools.knip",
25
+ "devtools.sync",
26
+ "devtools.status",
27
+ "devtools.workflows.sync",
28
+ "devtools.workflows.status",
29
+ "devtools.vscode.sync",
30
+ "devtools.vscode.status"
25
31
  ]
26
32
  },
27
33
  "keywords": [
@@ -29,15 +35,15 @@
29
35
  "eslint",
30
36
  "prettier",
31
37
  "knip",
32
- "linting",
33
- "formatting",
34
- "static-analysis",
38
+ "github-actions",
39
+ "vscode",
40
+ "repository-sync",
35
41
  "developer-tools"
36
42
  ],
37
43
  "bin": {
38
- "ankhorage-eslint": "./dist/eslint-cli.js",
39
- "ankhorage-knip": "./dist/knip-cli.js",
40
- "ankhorage-prettier": "./dist/prettier-cli.js"
44
+ "ankhorage-eslint": "./dist/cli/bin/eslint.js",
45
+ "ankhorage-knip": "./dist/cli/bin/knip.js",
46
+ "ankhorage-prettier": "./dist/cli/bin/prettier.js"
41
47
  },
42
48
  "exports": {
43
49
  "./cli": {
@@ -45,15 +51,15 @@
45
51
  "import": "./dist/cli/index.js"
46
52
  },
47
53
  "./eslint": {
48
- "types": "./dist/eslint.d.ts",
49
- "import": "./dist/eslint.js"
54
+ "types": "./dist/tools/eslint/index.d.ts",
55
+ "import": "./dist/tools/eslint/index.js"
50
56
  },
51
57
  "./knip": {
52
- "types": "./dist/knip.d.ts",
53
- "import": "./dist/knip.js"
58
+ "types": "./dist/tools/knip/index.d.ts",
59
+ "import": "./dist/tools/knip/index.js"
54
60
  },
55
61
  "./prettier": {
56
- "require": "./dist/prettier.cjs"
62
+ "require": "./dist/tools/prettier/index.cjs"
57
63
  }
58
64
  },
59
65
  "files": [
@@ -63,7 +69,7 @@
63
69
  "examples"
64
70
  ],
65
71
  "scripts": {
66
- "build": "rm -rf dist tsconfig.tsbuildinfo && tsc && cp src/prettier.cjs dist/prettier.cjs",
72
+ "build": "rm -rf dist tsconfig.tsbuildinfo && tsc && mkdir -p dist/tools/prettier dist/tools/workflows dist/tools/vscode && cp src/tools/prettier/index.cjs dist/tools/prettier/index.cjs && cp -R src/tools/workflows/files dist/tools/workflows/files && cp -R src/tools/vscode/files dist/tools/vscode/files",
67
73
  "typecheck": "bun x tsc --noEmit -p tsconfig.test.json",
68
74
  "doctor": "ankhorage-doctor validate .",
69
75
  "knip": "knip",
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- import { runStandaloneDevtoolsCommand } from './internal/runStandaloneDevtoolsCommand.js';
3
- const result = await runStandaloneDevtoolsCommand('lint', process.argv.slice(2));
4
- process.exit(result.exitCode);
@@ -1,11 +0,0 @@
1
- export type DevtoolsToolName = 'format' | 'knip' | 'lint';
2
- export interface DevtoolsCommandDefinition {
3
- path: readonly [DevtoolsToolName];
4
- capability: 'devtools.format' | 'devtools.knip' | 'devtools.lint';
5
- summary: string;
6
- packageName: 'eslint' | 'knip' | 'prettier';
7
- binName: 'eslint' | 'knip' | 'prettier';
8
- }
9
- export declare function getDevtoolsCommands(): readonly DevtoolsCommandDefinition[];
10
- export declare function findDevtoolsCommandByPath(path: readonly string[]): DevtoolsCommandDefinition | null;
11
- export declare function getDevtoolsCommand(toolName: DevtoolsToolName): DevtoolsCommandDefinition;
@@ -1,39 +0,0 @@
1
- const DEVTOOLS_COMMANDS = [
2
- {
3
- path: ['lint'],
4
- capability: 'devtools.lint',
5
- summary: 'Run the shared ESLint toolchain.',
6
- packageName: 'eslint',
7
- binName: 'eslint',
8
- },
9
- {
10
- path: ['format'],
11
- capability: 'devtools.format',
12
- summary: 'Run the shared Prettier toolchain.',
13
- packageName: 'prettier',
14
- binName: 'prettier',
15
- },
16
- {
17
- path: ['knip'],
18
- capability: 'devtools.knip',
19
- summary: 'Run the shared Knip toolchain.',
20
- packageName: 'knip',
21
- binName: 'knip',
22
- },
23
- ];
24
- export function getDevtoolsCommands() {
25
- return DEVTOOLS_COMMANDS;
26
- }
27
- export function findDevtoolsCommandByPath(path) {
28
- if (path.length !== 1) {
29
- return null;
30
- }
31
- return DEVTOOLS_COMMANDS.find((command) => command.path[0] === path[0]) ?? null;
32
- }
33
- export function getDevtoolsCommand(toolName) {
34
- const command = DEVTOOLS_COMMANDS.find((candidate) => candidate.path[0] === toolName);
35
- if (command === undefined) {
36
- throw new Error(`Unknown devtools command: ${toolName}`);
37
- }
38
- return command;
39
- }