Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ Monorepo (pnpm workspaces) for VS Code extension around [npmx.dev](https://npmx.
## Code Style
- ESM, strict TypeScript — never use `any` or type-cast with `as`; validate rather than assert
- Imports: type imports first, then `#` aliases (`#state`, `#utils/`, `#core/`), then external packages, then relative — no blank lines between groups
- No `node:` built-in imports in `src/` (browser-compat constraint); use `semver` subpath imports (not bare `semver`)
- No `node:` built-in imports in `src/` (browser-compat constraint)
- Naming: files/folders `kebab-case`, tests `*.test.ts`, functions `camelCase`, constants `SCREAMING_SNAKE_CASE`, types `PascalCase`
- Commits: [Conventional Commits](https://www.conventionalcommits.org/) — `type(scope): description` (lowercase subject)
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"devDependencies": {
"@types/node": "catalog:dev",
"@types/path-browserify": "catalog:dev",
"@types/semver": "catalog:dev",
"@vida0905/eslint-config": "catalog:dev",
"eslint": "catalog:dev",
"husky": "catalog:dev",
Expand Down
2 changes: 1 addition & 1 deletion packages/language-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"dependencies": {
"ocache": "catalog:inline",
"semver": "catalog:inline"
"verkit": "catalog:inline"
},
"devDependencies": {
"fast-npm-meta": "catalog:inline",
Expand Down
1 change: 1 addition & 0 deletions packages/language-core/src/utils/package.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ describe('resolveExactVersion', () => {
['latest', '4.10.0'],
['next', '4.11.0-beta.1'],
['beta', null],
['not a range', null],
])('should resolve $0 to $1', (spec, version) => {
const pkg = {
distTags: {
Expand Down
11 changes: 4 additions & 7 deletions packages/language-core/src/utils/package.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type { PackageInfo } from '../api/package'
import Range from 'semver/classes/range'
import gt from 'semver/functions/gt'
import lte from 'semver/functions/lte'
import satisfies from 'semver/functions/satisfies'
import { isGreater, isLessOrEqual, parseRange, satisfies } from 'verkit'

export function formatPackageId(name: string, version: string): string {
return `${name}@${version}`
Expand Down Expand Up @@ -67,7 +64,7 @@ function getMaxSatisfying(versions: string[], current: string, tags: PackageInfo
let version: string | null = null

try {
const range = new Range(current)
const range = parseRange(current)

let maxVersion: string | null = tags.latest
if (!satisfies(maxVersion, range))
Expand All @@ -77,8 +74,8 @@ function getMaxSatisfying(versions: string[], current: string, tags: PackageInfo
if (!satisfies(ver, range))
continue

if (!maxVersion || lte(ver, maxVersion)) {
if (!version || gt(ver, version)) {
if (!maxVersion || isLessOrEqual(ver, maxVersion)) {
if (!version || isGreater(ver, version)) {
version = ver
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/language-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"package-manager-detector": "1.7.0",
"path-browserify": "1.0.1",
"request-light": "0.7.0",
"semver": "7.8.5",
"verkit": "0.3.2",
"vscode-jsonrpc": "9.0.1",
"vscode-languageserver": "9.0.1",
"vscode-languageserver-protocol": "3.17.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/language-server/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default defineConfig({
'package-manager-detector',
'request-light',
'path-browserify',
'semver',
'verkit',
'ohash',
'ocache',
],
Expand Down
2 changes: 1 addition & 1 deletion packages/language-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@volar/language-service": "catalog:lsp",
"npmx-language-core": "workspace:*",
"npmx-shared": "workspace:*",
"semver": "catalog:inline",
"verkit": "catalog:inline",
"vscode-languageserver-textdocument": "catalog:inline",
"vscode-languageserver-types": "catalog:lsp",
"vscode-uri": "catalog:lsp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,10 @@ describe('resolveEngineMismatches', () => {
{ node: '>=18' },
{ node: 'lts' },
)).toEqual([])

expect(resolveEngineMismatches(
{ node: 'lts' },
{ node: '>=18' },
)).toEqual([])
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import type { Engines } from 'npmx-language-core/types'
import type { DiagnosticRule } from '../types'
import { npmxPackageUrl } from 'npmx-language-core/links'
import { formatPackageId, isPackageManifest } from 'npmx-language-core/utils'
import SemverRange from 'semver/classes/range'
import intersects from 'semver/ranges/intersects'
import subset from 'semver/ranges/subset'
import { isRangeSubset, parseRange, rangesIntersect } from 'verkit'
import { URI } from 'vscode-uri'

interface EngineMismatch {
Expand All @@ -27,17 +25,17 @@ export function resolveEngineMismatches(
continue

try {
const pkgRange = new SemverRange(packageRangeStr)
const depRange = new SemverRange(dependencyRangeStr)
const pkgRange = parseRange(packageRangeStr)
const depRange = parseRange(dependencyRangeStr)

if (subset(pkgRange, depRange))
if (isRangeSubset(pkgRange, depRange))
continue

mismatches.push({
engine,
packageRange: packageRangeStr,
dependencyRange: dependencyRangeStr,
hasIntersection: intersects(pkgRange, depRange),
hasIntersection: rangesIntersect(pkgRange, depRange),
})
} catch {
continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ describe('resolveUpgrade', () => {
expect(resolveUpgrade(...await createOptions('3.0.0-alpha.1'), [])).toBe('3.0.0-alpha.5')
})

it('should ignore build metadata when comparing versions', async () => {
const [dep, pkg] = await createOptions('^2.7.0')
expect(resolveUpgrade(dep, pkg, '2.7.0+build.1', [])).toBeUndefined()
})

it('should throw for an invalid resolved version', async () => {
const [dep, pkg] = await createOptions('^1.0.0')
expect(() => resolveUpgrade(dep, pkg, 'not a version', [])).toThrow(TypeError)
})

it('should not flag when target upgrade version is ignored', async () => {
expect(resolveUpgrade(...await createOptions('^1.0.0'), ['vite@^2.7.0'])).toBeUndefined()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import type { DependencyInfo } from 'npmx-language-core/workspace'
import type { DiagnosticRule } from '../types'
import { npmxPackageUrl } from 'npmx-language-core/links'
import { checkIgnored } from 'npmx-language-core/utils'
import gt from 'semver/functions/gt'
import lte from 'semver/functions/lte'
import prerelease from 'semver/functions/prerelease'
import { getPrerelease, isGreater, isLessOrEqual } from 'verkit'
import { formatUpgradeVersion } from '../../../utils/version'

export function resolveUpgrade(dep: DependencyInfo, pkg: PackageInfo, resolvedVersion: string, ignoreList: string[]) {
Expand All @@ -17,24 +15,24 @@ export function resolveUpgrade(dep: DependencyInfo, pkg: PackageInfo, resolvedVe
const { latest } = distTags
const { resolvedName } = dep

if (gt(latest, resolvedVersion)) {
if (isGreater(latest, resolvedVersion)) {
const targetVersion = formatUpgradeVersion(dep, latest)
if (checkIgnored({ ignoreList, name: resolvedName, version: targetVersion }))
return

return targetVersion
}

const currentPreId = prerelease(resolvedVersion)?.[0]
const currentPreId = getPrerelease(resolvedVersion)?.[0]
if (currentPreId == null)
return

for (const [tag, tagVersion] of Object.entries(distTags)) {
if (tag === 'latest')
continue
if (prerelease(tagVersion)?.[0] !== currentPreId)
if (getPrerelease(tagVersion)?.[0] !== currentPreId)
continue
if (lte(tagVersion, resolvedVersion))
if (isLessOrEqual(tagVersion, resolvedVersion))
continue
const targetVersion = formatUpgradeVersion(dep, tagVersion)
if (checkIgnored({ ignoreList, name: resolvedName, version: targetVersion }))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ describe('checkVulnerability', () => {
})
})

it('should suggest the greatest fixed version', async () => {
expect(await checkVulnerability(createVulnerabilityContext('pkg-fix-multi'), [])).toMatchObject({
message: expect.stringContaining('Upgrade to 2.0.0 to fix.'),
})
})

it('should throw when fixed versions contain invalid semver', async () => {
await expect(checkVulnerability(createVulnerabilityContext('pkg-fix-invalid'), [])).rejects.toThrow(TypeError)
})

it('should not flag when no vulnerabilities', async () => {
expect(await checkVulnerability(createVulnerabilityContext('pkg-safe'), [])).toBeUndefined()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { DiagnosticRule } from '../types'
import { getVulnerability, SEVERITY_LEVELS } from 'npmx-language-core/api/vulnerability'
import { npmxPackageUrl } from 'npmx-language-core/links'
import { checkIgnored, formatPackageId } from 'npmx-language-core/utils'
import lt from 'semver/functions/lt'
import { isLess } from 'verkit'
import { formatUpgradeVersion } from '../../../utils/version'

const DIAGNOSTIC_SEVERITY_MAPPING: Record<Exclude<OsvSeverityLevel, 'unknown'>, DiagnosticSeverity> = {
Expand All @@ -20,7 +20,7 @@ function getBiggestFixedInVersion(vulnerablePackages: PackageVulnerabilityInfo[]
if (depth !== 'root')
continue
for (const { fixedIn } of vulnerabilities) {
if (fixedIn && (!biggest || lt(biggest, fixedIn)))
if (fixedIn && (!biggest || isLess(biggest, fixedIn)))
biggest = fixedIn
}
}
Expand Down
38 changes: 38 additions & 0 deletions packages/language-service/tests/__setup__/msw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,44 @@ const vulnerabilityResults: Record<string, Record<string, unknown>> = {
failedQueries: 0,
totalCounts: { total: 1, critical: 0, high: 1, moderate: 0, low: 0 },
},
'pkg-fix-multi@1.0.0': {
package: 'pkg-fix-multi',
version: '1.0.0',
vulnerablePackages: [{
name: 'pkg-fix-multi',
version: '1.0.0',
depth: 'root',
path: [],
vulnerabilities: [
{ id: 'GHSA-1', summary: '', severity: 'high', aliases: [], url: '', fixedIn: '2.0.0' },
{ id: 'GHSA-2', summary: '', severity: 'high', aliases: [], url: '', fixedIn: '1.5.0' },
],
counts: { total: 2, critical: 0, high: 2, moderate: 0, low: 0 },
}],
deprecatedPackages: [],
totalPackages: 1,
failedQueries: 0,
totalCounts: { total: 2, critical: 0, high: 2, moderate: 0, low: 0 },
},
'pkg-fix-invalid@1.0.0': {
package: 'pkg-fix-invalid',
version: '1.0.0',
vulnerablePackages: [{
name: 'pkg-fix-invalid',
version: '1.0.0',
depth: 'root',
path: [],
vulnerabilities: [
{ id: 'GHSA-1', summary: '', severity: 'high', aliases: [], url: '', fixedIn: 'not a version' },
{ id: 'GHSA-2', summary: '', severity: 'high', aliases: [], url: '', fixedIn: '1.2.0' },
],
counts: { total: 2, critical: 0, high: 2, moderate: 0, low: 0 },
}],
deprecatedPackages: [],
totalPackages: 1,
failedQueries: 0,
totalCounts: { total: 2, critical: 0, high: 2, moderate: 0, low: 0 },
},
'pkg-safe@1.0.0': {
package: 'pkg-safe',
version: '1.0.0',
Expand Down
31 changes: 13 additions & 18 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ catalogs:
dev:
'@types/node': ^26.1.1
'@types/path-browserify': ^1.0.3
'@types/semver': ^7.7.1
'@vida0905/eslint-config': ^2.13.0
eslint: ^10.7.0
husky: ^9.1.7
Expand All @@ -32,7 +31,7 @@ catalogs:
package-manager-detector: ^1.7.0
path-browserify: ^1.0.1
reactive-vscode: ^1.0.2
semver: ^7.8.5
verkit: ^0.3.2
vscode-find-up: ^0.1.1
vscode-languageserver-textdocument: ^1.0.12
yaml: ^2.9.0
Expand Down