@squiz/validate-pinned-dependencies
v1.0.0
Published
Enforce strict dependency and override version pinning in package.json files across any project or monorepo.
Downloads
189
Maintainers
Keywords
Readme
@squiz/validate-pinned-dependencies
A robust, zero-dependency validation engine to enforce strict dependency and override version pinning in
package.jsonfiles across any Node.js project or monorepo.
📖 Table of Contents
- Why Pin Dependencies?
- Features
- Installation
- CLI Usage
- GitLab CI/CD Integration
- Husky / Pre-commit Hook Setup
- API Usage (As a Library)
- Local Development
- Contributing
- License
🛡️ Why Pin Dependencies?
Allowing flexible version ranges (e.g. ^ and ~) in application dependencies introduces non-determinism into your builds. This can lead to:
- Unexpected Build Failures: A minor or patch release of a nested third-party library can introduce bugs or breaking changes that break your build without any changes to your code.
- Security Vulnerabilities: If a package range is used, an attacker who hijacks a third-party dependency can publish a malicious patch version which is automatically pulled into your production environment.
- Harder Debugging: "It works on my machine, but fails in staging/production" is frequently caused by drift in dependencies resolved at different times.
By strictly pinning all third-party dependencies to exact versions (e.g. 1.2.3), you guarantee that every install produces the exact same dependency tree, leading to bulletproof reproducibility and containment.
✨ Features
- ⚡ Zero External Dependencies: Built entirely on native Node.js APIs to ensure it is lightweight and lightning-fast.
- 📂 Recursive Workspace Scanning: Automatically crawls directories to locate and validate all
package.jsonfiles. - ⚙️ Monorepo & Sibling Aware: Smart enough to bypass validation for local workspaces, sibling monorepo packages, or specific organizational scopes (e.g.
@squiz/*), allowing them to retain range dependencies for smooth workspace linking. - 🔗 Strict Override Validation: Inspects and validates the
overridessection ofpackage.jsonto ensure vulnerability overrides are strictly pinned. - 🤝 Peer-Dependency Exempt: Automatically ignores
peerDependenciessince they define compatibility ranges for host environments and should not be pinned. - 🛠️ Configurable & Flexible: Supports exclusion lists (
--allow), custom directory ignore paths, and custom internal scope definitions.
📥 Installation
Install the package as a development dependency in your project:
npm install --save-dev @squiz/validate-pinned-dependenciesOr run it directly on-demand using npx:
npx @squiz/validate-pinned-dependencies [options]💻 CLI Usage
The package exposes a validate-pinned-dependencies executable. Run it with --help to see all configuration parameters:
npx validate-pinned-dependencies --helpOptions
| Option | Shorthand | Description | Default |
|---|---|---|---|
| --dir <path> | -d | The directory to start searching for package.json files | . |
| --ignore <dirs> | -i | Comma-separated list of directories to ignore (appends to defaults) | See list below |
| --override-ignore <dirs> | | Comma-separated list of directories to ignore (replaces defaults) | None |
| --allow <packages> | -a | Comma-separated list of external packages allowed to have ranges | None |
| --internal-prefixes <pref>| -p | Comma-separated list of prefixes for internal packages | @squiz/ |
| --verbose | -v | Enable detailed logs and display file crawls during validation | false |
| --help | -h | Show the help menu and exit | |
Default Ignored Directories
node_modules, cdk.out, build, dist, .git, .changeset, .husky, coverage
Command Examples
1. Basic project-wide verification:
npx validate-pinned-dependencies2. Target a specific folder and enable verbose logging:
npx validate-pinned-dependencies --dir ./apps --verbose3. Exempt specific external dependencies that must use ranges:
npx validate-pinned-dependencies --allow react,react-dom🦊 GitLab CI/CD Integration
To guarantee that no unpinned dependencies make it to your primary branches, integrate the validation check into your GitLab CI pipeline.
Add the following job configuration to your .gitlab-ci.yml (under a test or lint stage):
stages:
- install
- test
# ... other configurations ...
validate:pinned-dependencies:
stage: test
extends: .setup-node # or whatever node installation anchor/template you use
interruptible: true
timeout: 5 minutes
needs:
- job: install_dependencies # depends on your install job name
script:
- npx @squiz/validate-pinned-dependencies
allow_failure: false
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_COMMIT_BRANCH == "main"🐶 Husky / Pre-commit Hook Setup
Catching unpinned dependencies locally before they are pushed to the remote repository ensures a frictionless development loop.
1. Install Husky
If you haven't already set up Husky in your project, install it and initialize:
npm install --save-dev husky
npx husky init2. Add Pre-Commit Hook
Create or append to your pre-commit hook file (.husky/pre-commit):
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
echo "🔍 Validating dependency pinning..."
npx @squiz/validate-pinned-dependenciesMake the hook script executable:
chmod +x .husky/pre-commitNow, every time a developer runs git commit, the validator will check all package.json files and block the commit if any unpinned third-party dependencies are found.
🔧 API Usage (As a Library)
You can import the core validation functions directly into your TypeScript/JavaScript files for custom scripting:
import { validatePackageJson, isPinned, findPackageJsonFiles } from '@squiz/validate-pinned-dependencies';
// Find package.json files
const files = findPackageJsonFiles('./my-project');
// Run individual file checks
const pkgJson = {
name: 'my-app',
dependencies: {
'lodash': '^4.17.21', // unpinned
'zod': '3.22.4' // pinned
}
};
const localPackages = new Set(['my-app', 'my-sibling-lib']);
const errors = validatePackageJson('package.json', pkgJson, localPackages, {
internalPrefixes: ['@squiz/', '@my-org/'],
allowRangesFor: ['lodash'] // exempt lodash from being checked
});
if (errors.length > 0) {
console.log('Errors found:', errors);
} else {
console.log('Validation passed!');
}🛠️ Local Development
Prerequisites
- Node.js >= 18.0.0
- npm >= 9.0.0
Dev Commands
Install dependencies:
npm installRun test suite:
npm testWatch tests during development:
npm run test:watchRun linter / TypeScript checks:
npm run lintAuto-format files:
npm run formatCompile TypeScript to JavaScript (dist/ directory):
npm run build🤝 Contributing
This repository is proprietary and internal to Squiz Group. Contributions and improvements are highly encouraged from any engineer within the company.
Please follow our internal development standards:
1. Branching & Workflows
- Always branching off of and targeting the
developbranch. - Create feature branches with descriptive names:
git checkout -b feat/short-slug develop
2. Code Quality & Standards
- Keep functions small, modular, and single-responsibility.
- Document all core logic and public APIs with JSDoc comments.
- Always add/update unit tests to cover new changes and edge cases.
- Verify everything compiles, lint checks pass, and tests succeed locally:
npm run lint && npm test
3. Commit Messages
- We strictly adhere to Conventional Commits using lowercase formatting:
feat(cli): add quiet mode optionfix(scanner): resolve crash on null package nametest(is-pinned): cover edge cases for beta tags
4. Merge Requests (MR)
- Push your branch to GitLab and open a Merge Request targeting the
developbranch. - Standard reviewers for this package are
halee,lnowak,trais, andjmatthew. - Ensure all CI/CD pipeline checks pass before requesting reviews.
📄 License
Proprietary and Confidential. Copyright (c) 2026 Squiz Group. All Rights Reserved.
Made with ❤️ by Squiz Content Intelligence Team.
