Skip to content
Back to skills

Js Static Check Strategy

ASecurity

Progressive `// @ts-check` per-leaf rollout playbook for legacy JS bundles. Covers the three-list legacy-globals sync (ESLint globals / TypeScript ambient .d.ts / JSDoc typedef), tsconfig exclude strategy for type-check-resistant files, leaf classification (typedef-widening-fixable vs permanent-exclude), line-anchored progress-grep traps, and Phase-2 exit-gate semantics. Use when planning a per-leaf cleanup PR, introducing a new leaf-level global, editing tsconfig''s exclude list, interpretin...

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 24, 2026
developmentjavascripttypescriptgojavabashnodeapifrontend

Works with

  • api

Security analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned September 24, 2026

npx -y skills add hmj1026/dhpk --skill js-static-check-strategy --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Js Static Check Strategy?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Js Static Check Strategy
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/hmj1026-js-static-check-strategy/badge)](https://www.skillsdirectory.com/skills/hmj1026-js-static-check-strategy)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: js-static-check-strategy
description: 'Progressive `// @ts-check` per-leaf rollout playbook for legacy JS bundles. Covers the three-list legacy-globals sync (ESLint globals / TypeScript ambient .d.ts / JSDoc typedef), tsconfig exclude strategy for type-check-resistant files, leaf classification (typedef-widening-fixable vs permanent-exclude), line-anchored progress-grep traps, and Phase-2 exit-gate semantics. Use when planning a per-leaf cleanup PR, introducing a new leaf-level global, editing tsconfig''s exclude list, interpreting /ts-check-status output, or validating the strict-checkJs exit gate. Not for everyday JavaScript feature work or isolated lint-rule lookups; use the sibling dhpk-js-lint-config and references/static-checks.md for those. Output: a per-leaf classification, three-list change set, and exit-gate evidence.'
metadata:
  dhpk-invocation-class: implicit-eligible
---

# JS static-check strategy — progressive rollout playbook

> Always-loaded SSOT rule: `modules/js/references/static-checks.md`.

This skill carries the long-form execution detail so the always-loaded rule
stays short. Load this when you are actually doing the rollout work.

## Legacy-globals — three-list sync

When a leaf introduces a new global, **all three lists must update together**
(no automatic derivation — three independent maintained files):

1. **`eslint.config.js`** `<projectName>LegacyGlobals` constant — readonly /
   writable annotation. SSOT for `no-undef`.
2. **`<frontend-root>/<project>-ambient.d.ts`** — `declare var X: any;`
   block. SSOT for `tsc --noEmit` bare-identifier resolution.
3. **`<frontend-root>/jsdoc-globals.js`** — `@typedef` (only when the
   default `any` from the ambient `.d.ts` is too loose and a real shape
   adds value).

A leaf PR that misses any of the three surfaces later as `no-undef` or
TS2304. CI catches drift after the fact; reviewers should still verify
three-way sync proactively.

## `// @ts-check` progressive strategy

- One leaf per PR: add `// @ts-check` + clean up the JSDoc + run the
  unit / contract tests to confirm no regression.
- Track the rollout against your mechanical-extraction cadence (Stage A,
  B, C…). Each stage produces a batch of leaves ready for type-check
  enablement.
- A stuck leaf can land `// @ts-nocheck` with a TODO comment as a transitional
  state — but every transitional file should have a tracked exit PR.

Measure progress with the **line-anchored** grep (anything looser is
gameable — see the trap below):

```bash
# Step 1 — strict opt-in (exit gate: this list must end up empty)
find <frontend-root> -maxdepth 1 -name '*.js' -exec \
    grep -lE '^[[:space:]]*//[[:space:]]*@ts-check[[:space:]]*$' {} \;

# Step 2 — files still on `// @ts-nocheck` (governance indicator;
# should trend toward zero, but some files may be permanent exclusions —
# see classification below).
find <frontend-root> -maxdepth 1 -name '*.js' -exec \
    grep -lE '^[[:space:]]*//[[:space:]]*@ts-nocheck' {} \;
```

`/ts-check-status` runs both queries plus a summary.

## Status mode owner

`$js-static-check-strategy status [--path <directory>]` owns the
`/dhpk:ts-check-status` status contract. With `--path`, resolve the directory
from the consumer repository's working directory; without it, use
`CLAUDE_PLUGIN_OPTION_JS_CHECK_PATH` or the `js/` default. Scan only immediate
regular `*.js` files, classify anchored `// @ts-check` and `// @ts-nocheck`
directives (with `nocheck` taking precedence), and report the deterministic
JSON summary `total=N strict=N nocheck=N unmarked=N` plus the file buckets.

A missing or unreadable scan path returns `status: UNAVAILABLE`, prints a
diagnostic, exits without fabricated counts, and is not a passing status. The
mode is read-only: it does not edit directives, configuration, or source
files. The package-local `scripts/status.js` runner accepts
`[--path <directory>]`, emits one JSON report on stdout, and exits `0` for a
completed `PASS` or `UNAVAILABLE` scan (argument/runner errors exit non-zero).

### Leaf classification template

When a leaf gets stuck on `// @ts-nocheck`, classify which exit path applies:

- **Typedef-widening fixable** — blocker is a too-narrow ambient `@typedef`
  conflicting with the leaf's constructor / inference pattern. **Exit
  path:** widen the typedef (or write a more precise JSDoc) in the
  per-leaf cleanup PR, then flip back to `// @ts-check`.
- **Permanent exclude** — blocker is cross-leaf late-init globals
  (typically the core monolith and a couple of helper modules sitting
  next to it). Widening the typedef doesn't help — the issue is runtime
  semantics, not a type definition. **Exit path:** add the file to
  `tsconfig.json`'s `exclude` array (same treatment as the truly massive
  leaves that resist any single-PR cleanup). Expected to live on
  `// @ts-nocheck` indefinitely.

Misclassifying a permanent-exclude file as "typical leaf cleanup" wastes
work. Look at the leaf's relationship to the monolith first.

### Trap — grep gameable

A bare `grep -l '@ts-check'` is a substring match. During the transitional
period, files often carry **both** `// @ts-nocheck` (the active directive)
**and** a comment mentioning `@ts-check` (e.g. `// TODO: enable @ts-check
after widening User typedef`). The bare grep matches the comment and
reports the file as already opted into strict mode.

The line-anchored form (`^\s*//\s*@ts-check\s*$`) only matches a dedicated
leading directive, which is the actual TypeScript opt-in trigger.

## `tsconfig.json` strategy

```jsonc
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": false,           // see note below
    "noEmit": true,
    "strict": false,
    "target": "ES2017",         // adjust to project's runtime baseline
    "module": "CommonJS",       // or ESNext for modern bundler projects
    "moduleResolution": "node",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "resolveJsonModule": false,
    "lib": ["ES2017", "DOM", "DOM.Iterable"]
  },
  "include": [
    "<frontend-root>/**/*.js",
    "<frontend-root>/**/*.d.ts",
    "<frontend-root>/jsdoc-globals.js"
  ],
  "exclude": [
    // The monolith bundle files and any leaf classified as "permanent exclude"
    "<frontend-root>/<monolith-entry>.js",
    "<frontend-root>/<helper-1>.js",
    "<frontend-root>/<helper-2>.js",
    "node_modules"
  ]
}
```

**`checkJs` design choice.** Two reasonable interpretations:

- **Strict project-wide** (`checkJs: true`) — every `.js` file is checked
  unless it carries `// @ts-nocheck`. Suitable when ≥ ~80% of leaves
  already have `// @ts-check`.
- **Per-leaf opt-in** (`checkJs: false`) — only files with `// @ts-check`
  are checked. Suitable when most leaves are still being audited.

Mid-rollout, **per-leaf opt-in wins**. Flip to project-wide once the
opt-in count hits the threshold above and the `// @ts-nocheck` set has
stabilised (only the permanent-exclude files remain).

## Ambient typedef standard

`<frontend-root>/jsdoc-globals.js` is where structural typedef SSOT lives:

- `@typedef` for the project namespace + its main sub-objects.
- `@typedef` for the canonical AJAX wrapper (`MyApp.api.request`,
  `MyApp.list.ajaxPromise`, etc.) — gives leaves accurate parameter /
  return types without per-leaf JSDoc.
- `@typedef` for major domain modules.

Leaf PRs annotate call sites via JSDoc `@type`, `@param`, `@returns`; TS
resolves cross-leaf types through the ambient typedef.

## Future evolution

| Work | Trigger |
|---|---|
| Migrating Tier 1.5 / Tier 1.7 files toward Tier 1 | One-by-one as their `$.ajax` / `axios` callsites get migrated |
| Promoting `checkJs` to `true` | When per-leaf strict opt-in reaches the threshold above |
| Linting `<script>` blocks inside server-side templates | Optional Phase-3 — usually scoped to the view files holding inline config / page-config SSOT |
| Bundler / ESM migration | Separate effort — once the modular structure is stable and the SSOT-facade has matured |

## Related rule

- `modules/js/references/static-checks.md` — always-loaded index pointing at
  this skill and the lint-config skill.
- `skills/dhpk-js-lint-config/SKILL.md` — companion skill with the
  ESLint tier model, custom AST selectors, and the legacy-globals
  three-list sync detail.

## When NOT to Use

Not for day-to-day lint-rule lookups (use the sibling `dhpk-js-lint-config` and
`references/static-checks.md`), and not for ESLint tier design itself. Load this
when actually executing the per-leaf `// @ts-check` rollout.

## Output

A per-leaf cleanup PR: one leaf flipped to `// @ts-check` with its three-list
globals updated, a correct `tsconfig.json` `exclude` for any permanent-exclude
leaf, and each stuck leaf classified (typedef-widening-fixable vs
permanent-exclude) with a tracked exit.

## Verification

- The line-anchored `^\s*//\s*@ts-check\s*$` grep (via `/ts-check-status`)
  counts the flipped leaf; the `// @ts-nocheck` set trends toward only the
  permanent-exclude files.
- `tsc --noEmit` passes after the flip; contract/unit tests show no regression.
- Any new global appears in all three lists (ESLint globals / ambient `.d.ts` /
  JSDoc typedef).

Files in this skill

  • SKILL.md9 KB
  • agents/openai.yaml265 B
  • references/frontend-review-patterns.md2.9 KB
  • references/static-checks.md3.3 KB
  • scripts/status.js4.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.