Skip to content

Commit

Permalink
fix: support nested resolutions and overrides (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
lneveu committed Feb 6, 2024
1 parent fd0220b commit 7e75208
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 15 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ export default defineConfig({
'unocss': 'ignore',
// regex starts and ends with '/'
'/vue/': 'latest'
},
// disable checking for "overrides" package.json field
depFields: {
overrides: false
}
})
```
Expand Down
37 changes: 31 additions & 6 deletions src/io/dependencies.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import type { DepType, RawDep, ResolvedDepChange } from '../types'

interface FlattenPkgData { [key: string]: { version: string, parents: string[] } }

function flatten(obj: any, parents: string[] = []): FlattenPkgData {
if (!obj)
return obj

let flattenData: FlattenPkgData = {}
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'object')
flattenData = { ...flattenData, ...flatten(value, [...parents, key]) }
else if (typeof value === 'string')
flattenData[key] = { version: value, parents }
}
return flattenData
}

export function getByPath(obj: any, path: string) {
return path.split('.').reduce((o, i) => o?.[i], obj)
return flatten(path.split('.').reduce((o, i) => o?.[i], obj))
}

export function setByPath(obj: any, path: string, value: any) {
Expand All @@ -12,30 +28,39 @@ export function setByPath(obj: any, path: string, value: any) {
}

export function parseDependencies(pkg: any, type: DepType, shouldUpdate: (name: string) => boolean): RawDep[] {
return Object.entries(getByPath(pkg, type) || {}).map(([name, version]) => parseDependency(name, version as string, type, shouldUpdate))
return Object.entries(getByPath(pkg, type) || {}).map(([name, { version, parents }]) => parseDependency(name, version, type, shouldUpdate, parents))
}

export function parseDependency(name: string, version: string, type: DepType, shouldUpdate: (name: string) => boolean): RawDep {
export function parseDependency(name: string, version: string, type: DepType, shouldUpdate: (name: string) => boolean, parents?: string[]): RawDep {
return {
name,
currentVersion: version,
parents,
source: type,
// when `updated` marked to `false`, it will be bypassed on resolving
update: shouldUpdate(name),
}
}

export function dumpDependencies(deps: ResolvedDepChange[], type: DepType) {
const data: Record<string, string> = {}
const data: Record<string, any> = {}
deps
.filter(i => i.source === type)
.sort((a, b) => a.name.localeCompare(b.name))
.forEach((i) => {
const version = i.update ? i.targetVersion : i.currentVersion
let targetLeaf = data

i.parents?.reduce((tree, parent) => {
tree[parent] ??= {}
targetLeaf = tree[parent]
return tree[parent]
}, data)

if (i.aliasName === undefined)
data[i.name] = version
targetLeaf[i.name] = version
else
data[i.aliasName] = `npm:${i.name}${version ? `@${version}` : ''}`
targetLeaf[i.aliasName] = `npm:${i.name}${version ? `@${version}` : ''}`
})

return data
Expand Down
16 changes: 15 additions & 1 deletion src/io/resolves.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { CheckOptions, DependencyFilter, DependencyResolvedCallback, DiffTy
import { diffSorter } from '../filters/diff-sorter'
import { getMaxSatisfying, getPrefixedVersion } from '../utils/versions'
import { getPackageMode } from '../utils/config'
import { parsePnpmPackagePath, parseYarnPackagePath } from '../utils/package'

const debug = {
cache: _debug('taze:cache'),
Expand Down Expand Up @@ -207,7 +208,20 @@ export async function resolveDependency(
}
}

const pkgData = await getPackageData(dep.name)
let resolvedName = dep.name

// manage Yarn resolutions (e.g. "foo@1/bar")
if (dep.source === 'resolutions') {
const packages = parseYarnPackagePath(dep.name)
resolvedName = packages.pop() ?? dep.name
}
// manage pnpm overrides (e.g. "foo@1>bar")
else if (dep.source === 'pnpm.overrides') {
const packages = parsePnpmPackagePath(dep.name)
resolvedName = packages.pop() ?? dep.name
}

const pkgData = await getPackageData(resolvedName)
const { tags, error } = pkgData
dep.pkgData = pkgData
let err: Error | string | null = null
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface RawDep {
currentVersion: string
source: DepType
update: boolean
parents?: string[]
}

export type DiffType = 'major' | 'minor' | 'patch' | 'error' | null
Expand Down
13 changes: 13 additions & 0 deletions src/utils/package.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Parse input string like `package-1/package-2` to an array of packages
*/
export function parseYarnPackagePath(input: string): string[] {
return input.match(/(@[^\/]+\/)?([^/]+)/g) || []
}

/**
* Parse input string like `package-1>package-2` to an array of packages
*/
export function parsePnpmPackagePath(input: string): string[] {
return input.match(/[^>]+/g) || []
}
49 changes: 41 additions & 8 deletions test/dumpDependencies.test.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
import { describe, expect, it } from 'vitest'
import { dumpDependencies } from '../src/io/dependencies'
import type { DepType, ResolvedDepChange } from '../src/types'
import type { ResolvedDepChange } from '../src/types'

describe('dumpDependencies', () => {
function getPackageBySource(source: DepType) {
function makeDepChange(source: string, override: Partial<ResolvedDepChange> = {}) {
return {
name: '@types/semver',
currentVersion: '^7.3.10',
source,
update: true,
targetVersion: '^7.3.12',
diff: 'patch',
...override,
} as ResolvedDepChange
}

it('dump `dependencies` type', () => {
const dump = dumpDependencies([getPackageBySource('dependencies')], 'dependencies')
const dump = dumpDependencies([makeDepChange('dependencies')], 'dependencies')
expect(dump).toMatchInlineSnapshot(`
{
"@types/semver": "^7.3.12",
}
`)
})
it('dump `devDependencies` type', () => {
const dump = dumpDependencies([getPackageBySource('devDependencies')], 'devDependencies')
const dump = dumpDependencies([makeDepChange('devDependencies')], 'devDependencies')
expect(dump).toMatchInlineSnapshot(`
{
"@types/semver": "^7.3.12",
}
`)
})
it('dump `pnpm.overrides` type', () => {
const dump = dumpDependencies([getPackageBySource('pnpm.overrides')], 'pnpm.overrides')
const dump = dumpDependencies([makeDepChange('pnpm.overrides')], 'pnpm.overrides')
expect(dump).toMatchInlineSnapshot(`
{
"@types/semver": "^7.3.12",
Expand All @@ -40,19 +41,51 @@ describe('dumpDependencies', () => {
})

it('dump `resolutions` type', () => {
const dump = dumpDependencies([getPackageBySource('resolutions')], 'resolutions')
const dump = dumpDependencies([
makeDepChange('resolutions'),
makeDepChange('resolutions', {
name: '@taze/pkg/@taze/nested-foo',
targetVersion: '^1.0.0',
}),
makeDepChange('resolutions', {
name: '@taze/pkg/@taze/[email protected]',
targetVersion: '^2.0.0',
}),
], 'resolutions')
expect(dump).toMatchInlineSnapshot(`
{
"@taze/pkg/@taze/[email protected]": "^2.0.0",
"@taze/pkg/@taze/nested-foo": "^1.0.0",
"@types/semver": "^7.3.12",
}
`)
})

it('dump `overrides` type', () => {
const dump = dumpDependencies([getPackageBySource('overrides')], 'overrides')
const dump = dumpDependencies([
makeDepChange('overrides', {
name: '@taze/nested-foo',
parents: ['@taze/pkg'],
targetVersion: '^1.0.0',
}),
makeDepChange('overrides', {
name: '@taze/nested-lvl2',
targetVersion: '^2.0.0',
parents: [
'@taze/pkg',
'@taze/nested-bar',
],
}),
], 'overrides')

expect(dump).toMatchInlineSnapshot(`
{
"@types/semver": "^7.3.12",
"@taze/pkg": {
"@taze/nested-bar": {
"@taze/nested-lvl2": "^2.0.0",
},
"@taze/nested-foo": "^1.0.0",
},
}
`)
})
Expand Down
107 changes: 107 additions & 0 deletions test/parseDependencies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ describe('parseDependencies', () => {
{
"currentVersion": "^4.13.19",
"name": "@taze/not-exists",
"parents": [],
"source": "dependencies",
"update": true,
},
{
"currentVersion": "npm:@types/web@^0.0.80",
"name": "@typescript/lib-dom",
"parents": [],
"source": "dependencies",
"update": true,
},
Expand All @@ -45,12 +47,14 @@ describe('parseDependencies', () => {
{
"currentVersion": "^4.13.19",
"name": "@taze/not-exists",
"parents": [],
"source": "devDependencies",
"update": true,
},
{
"currentVersion": "npm:@types/web@^0.0.80",
"name": "@typescript/lib-dom",
"parents": [],
"source": "devDependencies",
"update": true,
},
Expand All @@ -75,16 +79,119 @@ describe('parseDependencies', () => {
{
"currentVersion": "^4.13.19",
"name": "@taze/not-exists",
"parents": [],
"source": "pnpm.overrides",
"update": true,
},
{
"currentVersion": "npm:@types/web@^0.0.80",
"name": "@typescript/lib-dom",
"parents": [],
"source": "pnpm.overrides",
"update": true,
},
]
`)
})

it('parse package `resolutions`', () => {
const myPackage = {
name: '@taze/package1',
private: true,
resolutions: {
'@taze/not-exists': '^4.13.19',
'@typescript/lib-dom': 'npm:@types/web@^0.0.80',
'@taze/pkg/@taze/nested-foo': '^1.0.0',
'@taze/pkg/@taze/[email protected]': '^1.0.0',
},
}
const result = parseDependencies(myPackage, 'resolutions', () => true)
expect(result).toMatchInlineSnapshot(`
[
{
"currentVersion": "^4.13.19",
"name": "@taze/not-exists",
"parents": [],
"source": "resolutions",
"update": true,
},
{
"currentVersion": "npm:@types/web@^0.0.80",
"name": "@typescript/lib-dom",
"parents": [],
"source": "resolutions",
"update": true,
},
{
"currentVersion": "^1.0.0",
"name": "@taze/pkg/@taze/nested-foo",
"parents": [],
"source": "resolutions",
"update": true,
},
{
"currentVersion": "^1.0.0",
"name": "@taze/pkg/@taze/[email protected]",
"parents": [],
"source": "resolutions",
"update": true,
},
]
`)
})

it('parse package `overrides`', () => {
const myPackage = {
name: '@taze/package1',
private: true,
overrides: {
'@taze/not-exists': '^4.13.19',
'@typescript/lib-dom': 'npm:@types/web@^0.0.80',
'@taze/pkg': {
'@taze/nested-foo': '^1.0.0',
'@taze/nested-bar': {
'@taze/nested-lvl2': 'npm:@taze/override',
},
},
},
}
const result = parseDependencies(myPackage, 'overrides', () => true)
expect(result).toMatchInlineSnapshot(`
[
{
"currentVersion": "^4.13.19",
"name": "@taze/not-exists",
"parents": [],
"source": "overrides",
"update": true,
},
{
"currentVersion": "npm:@types/web@^0.0.80",
"name": "@typescript/lib-dom",
"parents": [],
"source": "overrides",
"update": true,
},
{
"currentVersion": "^1.0.0",
"name": "@taze/nested-foo",
"parents": [
"@taze/pkg",
],
"source": "overrides",
"update": true,
},
{
"currentVersion": "npm:@taze/override",
"name": "@taze/nested-lvl2",
"parents": [
"@taze/pkg",
"@taze/nested-bar",
],
"source": "overrides",
"update": true,
},
]
`)
})
})
Loading

0 comments on commit 7e75208

Please sign in to comment.