@ankhorage/devtools 1.1.1 → 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.
- package/README.md +165 -138
- package/dist/cli/bin/eslint.js +4 -0
- package/dist/cli/bin/knip.js +4 -0
- package/dist/cli/bin/prettier.js +4 -0
- package/dist/cli/commands.d.ts +25 -0
- package/dist/cli/commands.js +92 -0
- package/dist/cli/index.d.ts +5 -5
- package/dist/cli/index.js +3 -4
- package/dist/cli/runExternalTool.d.ts +31 -0
- package/dist/{internal/runDevtoolsCommand.js → cli/runExternalTool.js} +3 -3
- package/dist/cli/runProviderCommand.d.ts +8 -0
- package/dist/cli/runProviderCommand.js +11 -0
- package/dist/cli/runRepositoryCommand.d.ts +16 -0
- package/dist/cli/runRepositoryCommand.js +108 -0
- package/dist/cli/runStandaloneTool.d.ts +3 -0
- package/dist/cli/runStandaloneTool.js +5 -0
- package/dist/internal/readmeDocs.js +7 -7
- package/dist/{eslint.d.ts → tools/eslint/index.d.ts} +2 -4
- package/dist/{eslint.js → tools/eslint/index.js} +1 -5
- package/dist/tools/eslint/types.d.ts +18 -0
- package/dist/tools/knip/index.d.ts +20 -0
- package/dist/tools/shared/managedFiles.d.ts +20 -0
- package/dist/tools/shared/managedFiles.js +84 -0
- package/dist/tools/vscode/files/extensions.json +9 -0
- package/dist/tools/vscode/files/settings.json +15 -0
- package/dist/tools/vscode/index.d.ts +7 -0
- package/dist/tools/vscode/index.js +10 -0
- package/dist/tools/workflows/files/ci.yml +88 -0
- package/dist/tools/workflows/files/release.yml +65 -0
- package/dist/tools/workflows/index.d.ts +7 -0
- package/dist/tools/workflows/index.js +10 -0
- package/package.json +21 -15
- package/dist/eslint-cli.js +0 -4
- package/dist/internal/devtoolsCommands.d.ts +0 -11
- package/dist/internal/devtoolsCommands.js +0 -39
- package/dist/internal/runDevtoolsCommand.d.ts +0 -31
- package/dist/internal/runStandaloneDevtoolsCommand.d.ts +0 -3
- package/dist/internal/runStandaloneDevtoolsCommand.js +0 -5
- package/dist/knip-cli.js +0 -4
- package/dist/knip.d.ts +0 -20
- package/dist/prettier-cli.js +0 -4
- package/dist/types.d.ts +0 -18
- /package/dist/{eslint-cli.d.ts → cli/bin/eslint.d.ts} +0 -0
- /package/dist/{knip-cli.d.ts → cli/bin/knip.d.ts} +0 -0
- /package/dist/{prettier-cli.d.ts → cli/bin/prettier.d.ts} +0 -0
- /package/dist/{types.js → tools/eslint/types.js} +0 -0
- /package/dist/{knip.js → tools/knip/index.js} +0 -0
- /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
|
+
}
|
|
@@ -2,13 +2,13 @@ const REQUIRED_README_SNIPPETS = [
|
|
|
2
2
|
'ankh devtools lint',
|
|
3
3
|
'ankh devtools format',
|
|
4
4
|
'ankh devtools knip',
|
|
5
|
-
'
|
|
6
|
-
'
|
|
7
|
-
'
|
|
8
|
-
'devtools',
|
|
9
|
-
'devtools.
|
|
10
|
-
'devtools.
|
|
11
|
-
'
|
|
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)
|
|
@@ -3,9 +3,7 @@ import type { DevtoolsConfigOptions } from './types.js';
|
|
|
3
3
|
export declare const defaultIgnores: readonly ["**/ios/**", "**/android/**", "**/dist/**", "**/build/**", "**/.expo/**", "**/.next/**", "**/node_modules/**", "**/*.d.ts", "**/templates/**", "**/files/**"];
|
|
4
4
|
export declare const defaultRestrictedImports: readonly [{
|
|
5
5
|
readonly name: "react-native-reanimated-dnd";
|
|
6
|
-
readonly message: "Forbidden in
|
|
7
|
-
}, {
|
|
8
|
-
readonly name: "@ankhorage/react-native-reanimated-dnd-web";
|
|
9
|
-
readonly message: "Forbidden in monorepo. Use '@ankh/dnd' (boundary) instead.";
|
|
6
|
+
readonly message: "Forbidden in Ankhorage packages. Use '@ankhorage/react-native-reanimated-dnd-web' directly.";
|
|
10
7
|
}];
|
|
11
8
|
export declare function createConfig(options: DevtoolsConfigOptions): ReturnType<typeof tseslint.config>;
|
|
9
|
+
export type { DevtoolsConfigOptions, FlatConfigItem } from './types.js';
|
|
@@ -20,11 +20,7 @@ export const defaultIgnores = [
|
|
|
20
20
|
export const defaultRestrictedImports = [
|
|
21
21
|
{
|
|
22
22
|
name: 'react-native-reanimated-dnd',
|
|
23
|
-
message: "Forbidden in
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
name: '@ankhorage/react-native-reanimated-dnd-web',
|
|
27
|
-
message: "Forbidden in monorepo. Use '@ankh/dnd' (boundary) instead.",
|
|
23
|
+
message: "Forbidden in Ankhorage packages. Use '@ankhorage/react-native-reanimated-dnd-web' directly.",
|
|
28
24
|
},
|
|
29
25
|
];
|
|
30
26
|
export function createConfig(options) {
|
|
@@ -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,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,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,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.
|
|
4
|
-
"description": "Shared
|
|
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
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
38
|
+
"github-actions",
|
|
39
|
+
"vscode",
|
|
40
|
+
"repository-sync",
|
|
35
41
|
"developer-tools"
|
|
36
42
|
],
|
|
37
43
|
"bin": {
|
|
38
|
-
"ankhorage-eslint": "./dist/eslint
|
|
39
|
-
"ankhorage-knip": "./dist/knip
|
|
40
|
-
"ankhorage-prettier": "./dist/prettier
|
|
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",
|
package/dist/eslint-cli.js
DELETED
|
@@ -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;
|