@rtorcato/js-tooling 2.14.0 → 2.15.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.
@@ -529,11 +529,27 @@ async function checkDependabot(dir) {
529
529
  };
530
530
  }
531
531
  }
532
+ for (const candidate of [
533
+ 'renovate.json',
534
+ 'renovate.json5',
535
+ '.github/renovate.json',
536
+ '.github/renovate.json5',
537
+ '.renovaterc',
538
+ '.renovaterc.json',
539
+ ]) {
540
+ if (await fs.pathExists(path.join(dir, candidate))) {
541
+ return {
542
+ check: 'Dependabot',
543
+ status: 'ok',
544
+ detail: `${candidate} found (Renovate)`,
545
+ };
546
+ }
547
+ }
532
548
  return {
533
549
  check: 'Dependabot',
534
550
  status: 'optional-missing',
535
- detail: 'no .github/dependabot.yml',
536
- hint: 'Run `npx @rtorcato/js-tooling fix dependabot` to scaffold weekly dep updates',
551
+ detail: 'no Dependabot or Renovate config',
552
+ hint: 'Run `npx @rtorcato/js-tooling fix dependabot` (or `fix renovate`) to scaffold weekly dep updates',
537
553
  };
538
554
  }
539
555
  async function checkCodeQL(dir) {
@@ -110,6 +110,7 @@ export function lockfilePatchForTarget(target, lock) {
110
110
  case 'semantic-release':
111
111
  return c.semanticRelease ? null : { semanticRelease: true };
112
112
  case 'dependabot':
113
+ case 'renovate':
113
114
  case 'codeql':
114
115
  return c.securityAutomation ? null : { securityAutomation: true };
115
116
  case 'tsconfig':
@@ -10,7 +10,7 @@ import { generateESLintConfig, generatePrettierConfig } from '../generators/lint
10
10
  import { ensureEnginesNode, generateCodeowners, generateEditorConfig, generateKnipConfig, generateNvmrc, generateSizeLimitConfig, } from '../generators/misc.js';
11
11
  import { generateConfigs } from '../generators/index.js';
12
12
  import { composeVerifyScriptFromPkg } from '../generators/package-json.js';
13
- import { generateCodeQLWorkflow, generateDependabotConfig } from '../generators/security.js';
13
+ import { generateCodeQLWorkflow, generateDependabotConfig, generateRenovateConfig, } from '../generators/security.js';
14
14
  import { generateVitestConfig } from '../generators/testing.js';
15
15
  import { generateTreeshakeCheck, inferSubpathsFromExports } from '../generators/treeshake.js';
16
16
  import { copyPreset } from '../utils/copy-preset.js';
@@ -222,6 +222,17 @@ const FIXERS = [
222
222
  return { filesWritten: ['.github/dependabot.yml'] };
223
223
  },
224
224
  },
225
+ {
226
+ target: 'renovate',
227
+ description: 'Scaffold renovate.json (weekly schedule; alternative to Dependabot)',
228
+ appliesTo: ['Dependabot'],
229
+ outputs: ['renovate.json'],
230
+ riskLevel: 'safe-add',
231
+ async run({ targetDir }) {
232
+ await generateRenovateConfig(targetDir);
233
+ return { filesWritten: ['renovate.json'] };
234
+ },
235
+ },
225
236
  {
226
237
  target: 'codeql',
227
238
  description: 'Scaffold .github/workflows/codeql.yml (security scanning)',
@@ -393,6 +404,18 @@ const FIXERS = [
393
404
  export function getFixers() {
394
405
  return FIXERS;
395
406
  }
407
+ async function ownOutputsPresent(targetDir, fixer) {
408
+ for (const out of fixer.outputs) {
409
+ // Outputs that reference a package.json field (e.g. "package.json (scripts.verify)")
410
+ // can't be cheaply file-checked here; treat as present so we don't accidentally
411
+ // re-run safe-merge fixers on every targeted invocation.
412
+ if (out.includes('('))
413
+ return true;
414
+ if (await fs.pathExists(path.join(targetDir, out)))
415
+ return true;
416
+ }
417
+ return false;
418
+ }
396
419
  function findFixer(target) {
397
420
  const normalized = target.toLowerCase();
398
421
  return FIXERS.find((f) => f.target.toLowerCase() === normalized);
@@ -587,7 +610,14 @@ export async function fixCommand(target, options = {}) {
587
610
  // fixable when the user explicitly targets it — treat it as optional-missing
588
611
  // so the override + lockfile resync paths run.
589
612
  const lockfileDemoted = lock !== null && declinedInLock(lock, result.check);
590
- const effectiveResult = result.status === 'ok' && lockfileDemoted ? { ...result, status: 'optional-missing' } : result;
613
+ // When multiple fixers share a check (e.g. dependabot + renovate both apply to
614
+ // "Dependabot" deps-update coverage), the check can be `ok` from a sibling tool
615
+ // while this fixer's own outputs are still absent. In that case, treat as missing
616
+ // so the targeted scaffold runs.
617
+ const fixerOutputsPresent = await ownOutputsPresent(targetDir, fixer);
618
+ const effectiveResult = result.status === 'ok' && (lockfileDemoted || !fixerOutputsPresent)
619
+ ? { ...result, status: 'optional-missing' }
620
+ : result;
591
621
  if (effectiveResult.status === 'ok') {
592
622
  actions.push(recordFor(fixer.target, result.check, 'ok', 'already-ok', []));
593
623
  if (json)
@@ -24,6 +24,28 @@ updates:
24
24
  `;
25
25
  await fs.writeFile(filepath, content);
26
26
  }
27
+ export async function generateRenovateConfig(targetDir) {
28
+ const filepath = path.join(targetDir, 'renovate.json');
29
+ const content = `${JSON.stringify({
30
+ $schema: 'https://docs.renovatebot.com/renovate-schema.json',
31
+ extends: ['config:recommended', ':semanticCommits', ':semanticCommitTypeAll(chore)'],
32
+ schedule: ['before 4am on Monday'],
33
+ prConcurrentLimit: 10,
34
+ prHourlyLimit: 0,
35
+ rangeStrategy: 'bump',
36
+ packageRules: [
37
+ {
38
+ matchManagers: ['github-actions'],
39
+ commitMessagePrefix: 'chore(ci):',
40
+ },
41
+ {
42
+ matchManagers: ['npm'],
43
+ commitMessagePrefix: 'chore(deps):',
44
+ },
45
+ ],
46
+ }, null, 2)}\n`;
47
+ await fs.writeFile(filepath, content);
48
+ }
27
49
  export async function generateCodeQLWorkflow(targetDir) {
28
50
  await fs.ensureDir(path.join(targetDir, '.github', 'workflows'));
29
51
  const filepath = path.join(targetDir, '.github', 'workflows', 'codeql.yml');
package/dist/cli/index.js CHANGED
@@ -184,6 +184,12 @@ const TOOL_CATALOG = [
184
184
  exports: [],
185
185
  fixTarget: 'dependabot',
186
186
  },
187
+ {
188
+ name: 'Renovate',
189
+ description: 'Weekly automated dependency updates (alternative to Dependabot)',
190
+ exports: [],
191
+ fixTarget: 'renovate',
192
+ },
187
193
  {
188
194
  name: 'CodeQL',
189
195
  description: 'GitHub security scanning workflow',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtorcato/js-tooling",
3
- "version": "2.14.0",
3
+ "version": "2.15.0",
4
4
  "description": "JavaScript and TypeScript tooling for Node.js, React, Next.js, and Vitest.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -240,7 +240,7 @@
240
240
  "ts-jest": "^29.0.0",
241
241
  "tsup": "^8.0.0",
242
242
  "typescript": ">=5.0.0",
243
- "typescript-eslint": "*",
243
+ "typescript-eslint": "^8.0.0",
244
244
  "vitest": ">=3.0.0"
245
245
  },
246
246
  "peerDependenciesMeta": {