Skip to content

Commit bf21111

Browse files
committed
feat(validation): add guard against link: dependencies and remove from root
- Remove link: dependencies from root package.json (use workspace: or catalog: instead) - Add validate-no-link-deps.mjs script to detect and prevent link: protocol usage - Integrate validation into check script to run automatically - Link dependencies are prohibited: use workspace: for monorepo or catalog: for centralized versions This ensures consistent dependency management across the monorepo and prevents the use of link: which can cause issues with published packages.
1 parent c17755c commit bf21111

File tree

3 files changed

+177
-4
lines changed

3 files changed

+177
-4
lines changed

package.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22
"name": "socket-cli-monorepo",
33
"version": "0.0.0",
44
"private": true,
5-
"dependencies": {
6-
"socket-lib": "link:socket-lib",
7-
"socket-sdk-js": "link:socket-sdk-js"
8-
},
95
"devDependencies": {
106
"@babel/core": "7.28.4",
117
"@babel/parser": "7.28.4",

scripts/check.mjs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,31 @@ async function main() {
234234
}
235235
}
236236

237+
// Run link: validation check
238+
if (runAll) {
239+
if (!quiet) {
240+
getDefaultLogger().step('Validating no link: dependencies')
241+
}
242+
const validateResult = await spawn(
243+
'node',
244+
['scripts/validate-no-link-deps.mjs'],
245+
{
246+
shell: WIN32,
247+
stdio: quiet ? 'pipe' : 'inherit',
248+
},
249+
)
250+
if (validateResult.code !== 0) {
251+
if (!quiet) {
252+
getDefaultLogger().error('Validation failed')
253+
}
254+
process.exitCode = validateResult.code
255+
return
256+
}
257+
if (!quiet) {
258+
getDefaultLogger().error('')
259+
}
260+
}
261+
237262
if (!quiet) {
238263
getDefaultLogger().success('All checks passed')
239264
printFooter()

scripts/validate-no-link-deps.mjs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#!/usr/bin/env node
2+
/**
3+
* @fileoverview Validates that no package.json files contain link: dependencies.
4+
* Link dependencies are prohibited - use workspace: or catalog: instead.
5+
*/
6+
7+
import { promises as fs } from 'node:fs'
8+
import path from 'node:path'
9+
import { fileURLToPath } from 'node:url'
10+
11+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
12+
const rootPath = path.join(__dirname, '..')
13+
14+
/**
15+
* Find all package.json files in the repository.
16+
*/
17+
async function findPackageJsonFiles(dir) {
18+
const files = []
19+
const entries = await fs.readdir(dir, { withFileTypes: true })
20+
21+
for (const entry of entries) {
22+
const fullPath = path.join(dir, entry.name)
23+
24+
// Skip node_modules, .git, and build directories.
25+
if (
26+
entry.name === 'node_modules' ||
27+
entry.name === '.git' ||
28+
entry.name === 'build' ||
29+
entry.name === 'dist'
30+
) {
31+
continue
32+
}
33+
34+
if (entry.isDirectory()) {
35+
files.push(...(await findPackageJsonFiles(fullPath)))
36+
} else if (entry.name === 'package.json') {
37+
files.push(fullPath)
38+
}
39+
}
40+
41+
return files
42+
}
43+
44+
/**
45+
* Check if a package.json contains link: dependencies.
46+
*/
47+
async function checkPackageJson(filePath) {
48+
const content = await fs.readFile(filePath, 'utf8')
49+
const pkg = JSON.parse(content)
50+
51+
const violations = []
52+
53+
// Check dependencies.
54+
if (pkg.dependencies) {
55+
for (const [name, version] of Object.entries(pkg.dependencies)) {
56+
if (typeof version === 'string' && version.startsWith('link:')) {
57+
violations.push({
58+
file: filePath,
59+
field: 'dependencies',
60+
package: name,
61+
value: version,
62+
})
63+
}
64+
}
65+
}
66+
67+
// Check devDependencies.
68+
if (pkg.devDependencies) {
69+
for (const [name, version] of Object.entries(pkg.devDependencies)) {
70+
if (typeof version === 'string' && version.startsWith('link:')) {
71+
violations.push({
72+
file: filePath,
73+
field: 'devDependencies',
74+
package: name,
75+
value: version,
76+
})
77+
}
78+
}
79+
}
80+
81+
// Check peerDependencies.
82+
if (pkg.peerDependencies) {
83+
for (const [name, version] of Object.entries(pkg.peerDependencies)) {
84+
if (typeof version === 'string' && version.startsWith('link:')) {
85+
violations.push({
86+
file: filePath,
87+
field: 'peerDependencies',
88+
package: name,
89+
value: version,
90+
})
91+
}
92+
}
93+
}
94+
95+
// Check optionalDependencies.
96+
if (pkg.optionalDependencies) {
97+
for (const [name, version] of Object.entries(pkg.optionalDependencies)) {
98+
if (typeof version === 'string' && version.startsWith('link:')) {
99+
violations.push({
100+
file: filePath,
101+
field: 'optionalDependencies',
102+
package: name,
103+
value: version,
104+
})
105+
}
106+
}
107+
}
108+
109+
return violations
110+
}
111+
112+
async function main() {
113+
const packageJsonFiles = await findPackageJsonFiles(rootPath)
114+
const allViolations = []
115+
116+
for (const file of packageJsonFiles) {
117+
const violations = await checkPackageJson(file)
118+
allViolations.push(...violations)
119+
}
120+
121+
if (allViolations.length > 0) {
122+
console.error('❌ Found link: dependencies (prohibited)')
123+
console.error('')
124+
console.error(
125+
'Use workspace: protocol for monorepo packages or catalog: for centralized versions.',
126+
)
127+
console.error('')
128+
129+
for (const violation of allViolations) {
130+
const relativePath = path.relative(rootPath, violation.file)
131+
console.error(` ${relativePath}`)
132+
console.error(
133+
` ${violation.field}.${violation.package}: "${violation.value}"`,
134+
)
135+
}
136+
137+
console.error('')
138+
console.error('Replace link: with:')
139+
console.error(' - workspace: for monorepo packages')
140+
console.error(' - catalog: for centralized version management')
141+
console.error('')
142+
143+
process.exitCode = 1
144+
} else {
145+
console.log('✓ No link: dependencies found')
146+
}
147+
}
148+
149+
main().catch(error => {
150+
console.error('Validation failed:', error)
151+
process.exitCode = 1
152+
})

0 commit comments

Comments
 (0)