@rtorcato/js-tooling 2.11.0 ā 2.12.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.
|
@@ -630,6 +630,23 @@ function checkLockfile(lock) {
|
|
|
630
630
|
detail: `.js-tooling.json v${lock.version} (written by ${lock.writtenBy})`,
|
|
631
631
|
};
|
|
632
632
|
}
|
|
633
|
+
async function checkCodeowners(dir) {
|
|
634
|
+
for (const candidate of ['CODEOWNERS', '.github/CODEOWNERS', 'docs/CODEOWNERS']) {
|
|
635
|
+
if (await fs.pathExists(path.join(dir, candidate))) {
|
|
636
|
+
return {
|
|
637
|
+
check: 'CODEOWNERS',
|
|
638
|
+
status: 'ok',
|
|
639
|
+
detail: `${candidate} found`,
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
check: 'CODEOWNERS',
|
|
645
|
+
status: 'optional-missing',
|
|
646
|
+
detail: 'no CODEOWNERS file',
|
|
647
|
+
hint: 'Run `npx @rtorcato/js-tooling fix codeowners` to scaffold .github/CODEOWNERS',
|
|
648
|
+
};
|
|
649
|
+
}
|
|
633
650
|
async function checkGitLabCI(dir) {
|
|
634
651
|
for (const candidate of ['.gitlab-ci.yml', '.gitlab-ci.yaml']) {
|
|
635
652
|
if (await fs.pathExists(path.join(dir, candidate))) {
|
|
@@ -672,6 +689,7 @@ export async function runDoctor(dir) {
|
|
|
672
689
|
results.push(await checkDependabot(targetDir));
|
|
673
690
|
results.push(await checkCodeQL(targetDir));
|
|
674
691
|
results.push(await checkGitLabCI(targetDir));
|
|
692
|
+
results.push(await checkCodeowners(targetDir));
|
|
675
693
|
results.push(await checkTreeshakeSetup(targetDir, pkg));
|
|
676
694
|
// Lockfile-driven demotion: if the lock records an intentional opt-out for a
|
|
677
695
|
// check that's currently optional-missing, demote it to ok with a clear detail.
|
package/dist/cli/commands/fix.js
CHANGED
|
@@ -6,7 +6,7 @@ import { generateSemanticReleaseConfig } from '../generators/build.js';
|
|
|
6
6
|
import { generateCommitlintConfig, generateHuskyConfig, generatePrePushHook, } from '../generators/git.js';
|
|
7
7
|
import { generateGitHubActions } from '../generators/github-actions.js';
|
|
8
8
|
import { generateESLintConfig, generatePrettierConfig } from '../generators/linting.js';
|
|
9
|
-
import { ensureEnginesNode, generateEditorConfig, generateKnipConfig, generateNvmrc, generateSizeLimitConfig, } from '../generators/misc.js';
|
|
9
|
+
import { ensureEnginesNode, generateCodeowners, generateEditorConfig, generateKnipConfig, generateNvmrc, generateSizeLimitConfig, } from '../generators/misc.js';
|
|
10
10
|
import { composeVerifyScriptFromPkg } from '../generators/package-json.js';
|
|
11
11
|
import { generateCodeQLWorkflow, generateDependabotConfig } from '../generators/security.js';
|
|
12
12
|
import { generateVitestConfig } from '../generators/testing.js';
|
|
@@ -229,6 +229,18 @@ const FIXERS = [
|
|
|
229
229
|
return { filesWritten: ['.github/workflows/codeql.yml'] };
|
|
230
230
|
},
|
|
231
231
|
},
|
|
232
|
+
{
|
|
233
|
+
target: 'codeowners',
|
|
234
|
+
description: 'Scaffold .github/CODEOWNERS with commented examples',
|
|
235
|
+
appliesTo: ['CODEOWNERS'],
|
|
236
|
+
outputs: ['.github/CODEOWNERS'],
|
|
237
|
+
riskLevel: 'safe-add',
|
|
238
|
+
canFixDrift: false,
|
|
239
|
+
async run({ targetDir }) {
|
|
240
|
+
const written = await generateCodeowners(targetDir);
|
|
241
|
+
return { filesWritten: [written] };
|
|
242
|
+
},
|
|
243
|
+
},
|
|
232
244
|
{
|
|
233
245
|
target: 'editorconfig',
|
|
234
246
|
description: 'Scaffold .editorconfig (UTF-8, LF, tab indent)',
|
|
@@ -380,6 +392,16 @@ function logTargets() {
|
|
|
380
392
|
console.log(` ${chalk.green('ā')} ${chalk.bold(f.target)}: ${chalk.gray(f.description)}`);
|
|
381
393
|
}
|
|
382
394
|
}
|
|
395
|
+
export function listFixers() {
|
|
396
|
+
return FIXERS.map((f) => ({
|
|
397
|
+
target: f.target,
|
|
398
|
+
description: f.description,
|
|
399
|
+
appliesTo: f.appliesTo,
|
|
400
|
+
outputs: f.outputs,
|
|
401
|
+
riskLevel: f.riskLevel ?? 'destructive',
|
|
402
|
+
canFixDrift: f.canFixDrift ?? false,
|
|
403
|
+
}));
|
|
404
|
+
}
|
|
383
405
|
async function applyFixer(fixer, result, targetDir, pkg, lock, dryRun, silent) {
|
|
384
406
|
if (dryRun) {
|
|
385
407
|
if (!silent) {
|
|
@@ -442,6 +464,21 @@ export async function fixCommand(target, options = {}) {
|
|
|
442
464
|
// JSON mode implies --yes so prompts don't corrupt the output stream.
|
|
443
465
|
const assumeYes = options.yes === true || json;
|
|
444
466
|
const silent = json;
|
|
467
|
+
if (options.list) {
|
|
468
|
+
const summary = listFixers();
|
|
469
|
+
if (json) {
|
|
470
|
+
console.log(JSON.stringify({ targets: summary }, null, 2));
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
console.log(chalk.cyan('\nš§ Registered fix targets:\n'));
|
|
474
|
+
for (const f of summary) {
|
|
475
|
+
console.log(` ${chalk.green('ā')} ${chalk.bold(f.target)}`);
|
|
476
|
+
console.log(` ${chalk.gray(f.description)}`);
|
|
477
|
+
console.log(` ${chalk.dim(`risk=${f.riskLevel}, drift=${f.canFixDrift ? 'yes' : 'no'}, outputs=${f.outputs.join(', ')}`)}`);
|
|
478
|
+
}
|
|
479
|
+
console.log();
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
445
482
|
const pkg = await readPackageJson(targetDir);
|
|
446
483
|
const lock = await readLockfile(targetDir);
|
|
447
484
|
const results = await runDoctor(targetDir);
|
|
@@ -30,6 +30,20 @@ const SIZE_LIMIT_CONFIG = [
|
|
|
30
30
|
limit: '10 kB',
|
|
31
31
|
},
|
|
32
32
|
];
|
|
33
|
+
const CODEOWNERS_CONTENT = `# .github/CODEOWNERS
|
|
34
|
+
# Each line is a file pattern followed by one or more owners.
|
|
35
|
+
# Owners can be GitHub usernames (@user) or team names (@org/team).
|
|
36
|
+
# Order matters ā the last matching pattern wins.
|
|
37
|
+
# See https://docs.github.com/articles/about-code-owners/
|
|
38
|
+
#
|
|
39
|
+
# Examples:
|
|
40
|
+
# * @your-username
|
|
41
|
+
# /src/api/ @backend-team
|
|
42
|
+
# /docs/ @docs-team @your-username
|
|
43
|
+
# *.md @docs-team
|
|
44
|
+
|
|
45
|
+
* @TODO-owner
|
|
46
|
+
`;
|
|
33
47
|
export async function generateEditorConfig(targetDir) {
|
|
34
48
|
await fs.writeFile(path.join(targetDir, '.editorconfig'), EDITORCONFIG_CONTENT);
|
|
35
49
|
}
|
|
@@ -54,6 +68,12 @@ export async function generateKnipConfig(targetDir) {
|
|
|
54
68
|
export async function generateSizeLimitConfig(targetDir) {
|
|
55
69
|
await fs.writeJson(path.join(targetDir, '.size-limit.json'), SIZE_LIMIT_CONFIG, { spaces: 2 });
|
|
56
70
|
}
|
|
71
|
+
export async function generateCodeowners(targetDir) {
|
|
72
|
+
const filepath = path.join(targetDir, '.github', 'CODEOWNERS');
|
|
73
|
+
await fs.ensureDir(path.dirname(filepath));
|
|
74
|
+
await fs.writeFile(filepath, CODEOWNERS_CONTENT);
|
|
75
|
+
return '.github/CODEOWNERS';
|
|
76
|
+
}
|
|
57
77
|
export async function generateMiscBaseline(targetDir) {
|
|
58
78
|
await generateEditorConfig(targetDir);
|
|
59
79
|
await generateNvmrc(targetDir);
|
package/dist/cli/index.js
CHANGED
|
@@ -221,15 +221,20 @@ program
|
|
|
221
221
|
.option('--yes', 'Assume yes to all prompts (including drift overwrites)')
|
|
222
222
|
.option('--dry-run', 'Print what would change without writing files')
|
|
223
223
|
.option('--json', 'Emit machine-readable JSON output (implies --yes)')
|
|
224
|
+
.option('--list', 'List all registered fix targets and exit')
|
|
224
225
|
.action((target, options) => fixCommand(target, {
|
|
225
226
|
directory: options.directory,
|
|
226
227
|
yes: options.yes,
|
|
227
228
|
dryRun: options.dryRun,
|
|
228
229
|
json: options.json,
|
|
230
|
+
list: options.list,
|
|
229
231
|
}));
|
|
230
232
|
program.hook('preAction', async (_, actionCommand) => {
|
|
231
233
|
const name = actionCommand.name();
|
|
232
234
|
if (name === 'setup' || name === 'doctor' || name === 'fix') {
|
|
235
|
+
// `fix --list` is read-only and safe to run anywhere, including this repo.
|
|
236
|
+
if (name === 'fix' && actionCommand.opts().list)
|
|
237
|
+
return;
|
|
233
238
|
const dir = actionCommand.opts().directory ?? process.cwd();
|
|
234
239
|
if (await isSelfRepo(dir)) {
|
|
235
240
|
console.log(chalk.yellow('\nā ļø This command cannot be run inside the @rtorcato/js-tooling repo itself.\n'));
|