
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
A tool to safeguard package dependencies and TypeScript configuration hygiene in monorepos, based on rich policy examples and explicit reasons
A tool to safeguard package dependencies and TypeScript configuration hygiene in monorepos, based on rich policy examples and explicit reasons. Every finding explains why a rule must or should apply, keeping reviews focused and predictable. This helps automate reviews, clarify next steps, and connect findings directly to coding-assistant AI instructions. 🚥
import/no-internal-modules or no-restricted-imports); use dep-fence for cross-package boundaries and repo-level policies.external with peerDependencies. 📦➕📦❌skipLibCheck with explicit reasons or an allow‑list. ⚖️tsconfig healthy (baseline inheritance, forbid ../src direct references, JSX option sanity).It works alongside your existing linters, graph analyzers, dependency inventory tools, and package-publish validators. Together they cover in-file quality, dependency hygiene, and release readiness — while dep-fence enforces cross-package boundaries and provides CI-first policy guardrails.
pnpm-workspace.yaml or nearest package.json.packages/*/**/package.jsonapp/package.json (if present)ui, publishable/private, usesTsup, hasTsx, browser/node, worker, next, storybook, app.tsup.external from repo tsup.base.config.* and per‑package tsup.config.* when available.Local (recommended):
pnpm add -D dep-fence
# or
npm i -D dep-fence
Global:
npm i -g dep-fence
Requirement: Node.js >= 18
dep-fence # YAML output (default)
dep-fence --format pretty # human‑readable (pretty)
dep-fence --format yaml # YAML explicitly
dep-fence -f yaml -g severity # group by ERROR/WARN/INFO
dep-fence -f json # JSON output
dep-fence --strict # exit 1 if any ERROR
dep-fence -c path/to/dep-fence.config.ts # explicit policy file (TS/JS supported)
dep-fence -h # help
dep-fence -v # version
--format yaml --group-by package).--format pretty for human‑readable, package‑grouped output.--format json for machine‑readable output.--group-by <package|rule|severity> only affects YAML.
dep-fence --format yaml --group-by ruledep-fence --format yaml --group-by severityShort flags: -f = --format, -g = --group-by, -c = --config.
Note: the legacy --json flag was removed; use --format json.
pnpm dep-fence
pnpm dep-fence --strict # CI gate (exit 1 on ERROR)
Success Output
✔ No violations (0)
Violation Output in YAML format (default):
"@your/ui-button":
- type: ui-in-deps
severity: ERROR
message: |
UI libs should be peerDependencies (not dependencies):
- react
reason: UI packages should not bundle React/MUI; rely on host peers.
Violation Output in pretty format (with --format pretty option):
=== @your/ui-button ===
ERROR ui-in-deps: UI libs should be peerDependencies (not dependencies):
- react
Because: UI packages should not bundle React/MUI; rely on host peers.
Zero‑Config Mode runs the default package policies (package‑level checks) in a typical monorepo:
pnpm-workspace.yaml or package.json.packages/*/**/package.json and app/package.json (if present).tsconfig hygiene, and skipLibCheck governance.dep-fence.config.* required to get value on day one.Why this matters (concrete benefits):
--strict to fail on ERRORs when ready.dep-fence -f yaml -g severity > dep-fence.report.yaml
This helps leads/owners scan ERRORs vs WARNs separately and share the file in reviews.
dep-fence -f json | jq -r '.findings[] | select(.severity=="ERROR") | "[\(.severity)] \(.packageName) :: \(.rule)"'
This prints concise error lines like: [ERROR] @your/ui-button :: ui-in-deps.
Zero‑config works out of the box. When you need more control, provide an explicit policy file (dep-fence.config.ts or dep-fence.config.mjs) at the repo root to replace the defaults.
Policy file (TypeScript example):
// dep-fence.config.ts
import { all, isUI, isPublishable } from 'dep-fence/conditions';
import type { Policy } from 'dep-fence/types';
export const policies: Policy[] = [
{
id: 'ui-externals-and-peers',
when: all(isUI(), isPublishable()),
because: 'UI packages should not bundle React/MUI; rely on peers.',
rules: ['ui-in-deps', 'ui-missing-peer', 'peer-in-external']
}
];
You can also tweak severity per rule via severityOverride and compose complex conditions with all(...), any(...), and not(...). Helpers like isUI(), isPublishable(), and usesTsup() are available from dep-fence/conditions.
Repo‑wide operational settings (JSON) can be declared in dep-fence.config.json:
{
"allowSkipLibCheck": [
"@your/legacy-chart" // temporary exception
]
}
Per‑package justification for skipLibCheck can live in each tsconfig.json:
{
"compilerOptions": { "skipLibCheck": true },
"checkDeps": {
"allowSkipLibCheck": true,
"reason": "3rd‑party types temporary mismatch; scheduled fix"
}
}
Representative Policy Examples (Purpose / Snippet / Outcome)
Public API only (ban deep imports across packages)
@org/foo is OK; @org/foo/src/x is not.{ id: 'public-api-only', when: () => true, because: 'Only use package public entrypoints.', rules: [
{ rule: 'import-path-ban', options: { forbid: ['^@[^/]+/[^/]+/src/'] }, severity: 'ERROR' },
]}
src/.Peers × tsup externals alignment
{ id: 'tsup-peer-hygiene', when: isPublishable(), because: "Don't bundle peers.", rules: ['peer-in-external','external-in-deps'] }
tsup.external or duplicated in dependencies.Enforce types from dist
dist/*.d.ts.{ id: 'package-types-dist', when: isPublishable(), because: 'Expose types from dist/*.d.ts.', rules: ['package-types-dist'] }
types or exports[entry].types do not point to dist/*.d.ts.You can import pre-defined config files in the examples directory with --config option as needed.
Please consult examples/README.md for stacks/recipes oriented examples (React, Router v7, TypeScript v5, Bundlers). Legacy examples/policies/ paths have been retired; use the stacks/recipes paths.
You can create your own config files to customize the rules and settings.
Simple example 1:
import type { Policy } from 'dep-fence/types';
import { defaultPolicies } from 'dep-fence';
import { pkgUiPeersRule, pkgExportsExistRule, tsconfigHygieneRule } from 'dep-fence/guards';
const custon: Policy[] = [
pkgUiPeersRule({ exclude: ['@your/app'] }),
pkgExportsExistRule({ roots: ['packages', 'app'] }),
tsconfigHygieneRule({
skipLibCheck: { allowedPackages: ['@your/temp-exception'], requireReasonField: true, action: 'warn' },
}),
];
const policies: Policy[] = [...defaultPolicies, ...custom];
export default policies;
Simple example 2:
import type { Policy } from 'dep-fence/types';
export const policies: Policy[] = [
{
id: 'my-custom-checks',
when: isPublishable(),
because: 'repo‑specific validation',
rules: [{
rule: 'custom',
id: 'check-pkg-field',
run: (ctx) => {
const f = [] as any[];
if (!ctx.pkgJson.customField) {
f.push({ packageName: ctx.pkgName, packageDir: ctx.pkgDir, rule: 'check-pkg-field', severity: 'WARN', message: 'missing customField', because: ctx.because });
}
return f;
}
}]
}
];
Config format choice (TS or MJS)
dep-fence.config.ts, dep-fence.config.mjs (ESM .mjs preferred over .js)..mjs for zero‑setup CI/offline; prefer .ts for editor type‑safety..ts configs:
tsx): NODE_OPTIONS="--loader tsx" pnpm dep-fence.tsup): tsup dep-fence.config.ts --format esm --dts false --out-dir ..TS: dep-fence.config.ts
import { defaultPolicies } from 'dep-fence';
import type { Policy } from 'dep-fence/types';
const custom: Policy[] = [
{ id: 'ban-deep-imports', when: () => true, because: 'Use public API only; avoid cross‑package internals.', rules: [
{ rule: 'import-path-ban', options: { forbid: ['^@[^/]+/[^/]+/src/'] }, severity: 'ERROR' },
]},
];
export const policies: Policy[] = [...defaultPolicies, ...custom];
MJS: dep-fence.config.mjs
import { defaultPolicies } from 'dep-fence';
/** @type {import('dep-fence/types').Policy[]} */
export const policies = [
...defaultPolicies,
{ id: 'ban-deep-imports', when: () => true, because: 'Use public API only; avoid cross‑package internals.', rules: [
{ rule: 'import-path-ban', options: { forbid: ['^@[^/]+/[^/]+/src/'] }, severity: 'ERROR' },
]},
];
DEP_FENCE_CONFIG — absolute/relative path to a policies module (overrides file discovery).DEP_FENCE_REPO_CONFIG — path to a JSON file with repo‑wide settings (overrides dep-fence.config.json).DEP_FENCE_CONFIG for subtrees/teams if needed.Alongside package policies (see Zero‑Config Mode), dep‑fence ships lightweight repository‑level guards under the dep-fence/guards entry. They are designed for Git hooks (predictable, no hidden state):
allowed-dirs — Commit scope guard: staged files must be under allowed globs.mtime-compare — Advisory: detect files newer than your rules/SSOT baseline.upstream-conflict — Optimistic conflict detection: fail if upstream has other‑author changes touching protected paths since your base.New guards for monorepo publishing/build hygiene:
pkg-exports-exist — Verify package.json main/module/exports paths point to existing files (prevents publish/bundler breakage).pkg-ui-peers — Enforce UI singletons as peers and align bundler externals (flags: ui-in-deps, ui-missing-peer, peer-in-external, external-in-deps).tsconfig-hygiene — Keep tsconfig healthy (extends repo base, jsx option sanity, skipLibCheck governance with allowlist/justification).Try the examples:
pnpm dlx tsx examples/guards/run.ts --mode pre-commit
pnpm dlx tsx examples/guards/run.ts --mode pre-push
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.ui-peers.config.ts
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.pkg-exports.config.ts
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.tsconfig-hygiene.config.ts
# Additional guard presets (pair with recipes)
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.ui-peer-policy.config.ts
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.publishable-tsconfig-hygiene.config.ts
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.jsx-option-for-tsx.config.ts
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.tsconfig-paths.config.ts
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.maplibre-allowlist.config.ts
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.package-types-dist.config.ts
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.strict-ui.config.ts
pnpm dlx tsx examples/guards/run.ts --mode pre-commit \
--config examples/guards/guards.source-import-ban.config.ts
# GitHub Actions example
jobs:
dep-fence:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: pnpm i --frozen-lockfile
- run: pnpm dep-fence # refer to your package.json script
peer-in-external, source-import-ban).options and a computed severity; may be built‑in or a plugin (via create(options)=>check(ctx)).{ id, when, because, rules, options?, severityOverride? } — evaluated per package.ui-peer-policy rule + peer-in-external rule for publishable UI packages.examples/recipes/…/dep-fence.config.ts and is referenced via DEP_FENCE_CONFIG=….examples/guards/run.ts.See examples/README.md for a concise, up‑to‑date index of stacks and recipes. Typical invocations:
# Minimal policy set
DEP_FENCE_CONFIG=examples/recipes/minimal/dep-fence.config.ts pnpm dep-fence
# Focused examples (stacks/recipes paths)
DEP_FENCE_CONFIG=examples/recipes/tsconfig-paths/dep-fence.config.ts pnpm dep-fence
DEP_FENCE_CONFIG=examples/recipes/package-exports-guard/dep-fence.config.ts pnpm dep-fence
DEP_FENCE_CONFIG=examples/stacks/bundlers/vite/recipes/multi-entry-workers/dep-fence.config.ts pnpm dep-fence
# With repo‑wide JSON settings
DEP_FENCE_REPO_CONFIG=examples/repo-config/dep-fence.config.json pnpm dep-fence
UI policies — quick guide:
ui-peer-policy (recipe): package.json‑only check; fast to adopt; good first step.ui-peers-light (recipe): gentle variant; WARN by default; no bundler checks.minimal (stack): adds bundler externals alignment (tsup) on top of peers.strict-ui (recipe): strict peers + bundler alignment; suitable for CI gating in libraries.peer-in-external (peer missing from tsup.external)external-in-deps (external also listed in dependencies)ui-in-deps (React/MUI/Emotion in dependencies)ui-missing-peer (UI libs used but missing from peerDependencies)tsconfig-no-base (not extending repo base; hint)paths-direct-src (direct ../src references)jsx-mismatch (.tsx present but jsx not react-jsx)skipLibCheck governance
skipLibCheck-not-allowed (enabled without permission)skipLibCheck-no-reason (permitted but missing rationale)maplibre-direct-dep (direct MapLibre deps outside the wrapper package)All findings include “Because: …” to surface rationale.
Each default policy explains why it applies (Because) and targets specific attributes:
ui-externals-and-peers — UI packages must not bundle React/MUI; rely on peers. Rules: ui-in-deps, ui-missing-peer, peer-in-external.tsup-peer-hygiene — bundlers must externalize peers. Rules: peer-in-external, external-in-deps.publishable-tsconfig-hygiene — keep published tsconfig clean. Rules: tsconfig-no-base, paths-direct-src.publishable-local-shims — avoid long‑lived local *.d.ts. Rule: local-shims (default WARN; can be raised).jsx-option-for-tsx — TSX requires jsx: react-jsx. Rule: jsx-mismatch.skipLibCheck-governance — permissioned toggle with reasons. Rules: skipLibCheck-*.non-ui-paths-hygiene — discourage cross‑src refs broadly. Rule: paths-direct-src.maplibre-encapsulation — only wrapper package may depend on MapLibre. Rule: maplibre-direct-dep.Severity override example:
export const policies = [
{
id: 'ui-externals-and-peers',
when: all(isUI(), isPublishable()),
because: '…',
rules: ['ui-in-deps', 'ui-missing-peer', 'peer-in-external'],
severityOverride: { 'ui-missing-peer': 'ERROR' }
}
];
Additional rules and helpers can be enabled via a policy file:
source-import-ban — ban specific named imports from a module. See examples/recipes/source-import-ban/dep-fence.config.ts.tsconfig-paths — enforce paths to point to dist/*.d.ts and/or forbid patterns.package-exports-guard — guard subpaths (e.g., forbid types for ./workers/*).package-types-dist — ensure package types and exports[entry].types point to dist/*.d.ts.import { runWithPolicies, defaultPolicies } from 'dep-fence';
import { any, isPublishable } from 'dep-fence/conditions';
const findings = runWithPolicies(defaultPolicies);
const hasError = findings.some((f) => f.severity === 'ERROR');
Types are available from dep-fence/types (Finding, Policy, Condition, Severity, ...).
paths/bundler aliases.ESLint or dep‑fence?
Why not just dependency‑cruiser?
What are the best practices for using dep-fence?
severityOverride (e.g., WARN → ERROR) to roll out gradually without blocking early adoption.--strict to fail on ERRORs; locally, run without --strict to discover and review issues incrementally.How to allow a temporary exception?
severityOverride, and record a Because statement.How to protect publish quality?
Where does “unused dependency warning” fit?
depcheck/build graph tools, or author a custom guard similar to guards.package-types-dist.unused-deps and include it in a policy for publishable packages. This repo already documents plugin rules in docs/dep-fence-upstream-guide.md.Hiroya Kubo hiroya@cuc.ac.jp
MIT
Happy fencing! 🧱✨ Add reasons to rules and keep dependencies sane.
FAQs
A tool to safeguard package dependencies and TypeScript configuration hygiene in monorepos, based on rich policy examples and explicit reasons
We found that dep-fence demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.