@kubb/core 5.0.0-alpha.5 → 5.0.0-alpha.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/PluginDriver-DtwggkXg.cjs +1082 -0
- package/dist/PluginDriver-DtwggkXg.cjs.map +1 -0
- package/dist/PluginDriver-mXeqWp-U.js +979 -0
- package/dist/PluginDriver-mXeqWp-U.js.map +1 -0
- package/dist/index.cjs +1013 -1829
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +274 -265
- package/dist/index.js +1003 -1799
- package/dist/index.js.map +1 -1
- package/dist/mocks.cjs +138 -0
- package/dist/mocks.cjs.map +1 -0
- package/dist/mocks.d.ts +74 -0
- package/dist/mocks.js +133 -0
- package/dist/mocks.js.map +1 -0
- package/dist/types-DfEv9d_c.d.ts +1721 -0
- package/package.json +51 -57
- package/src/FileManager.ts +133 -0
- package/src/FileProcessor.ts +86 -0
- package/src/Kubb.ts +154 -101
- package/src/PluginDriver.ts +418 -0
- package/src/constants.ts +43 -47
- package/src/createAdapter.ts +25 -0
- package/src/createKubb.ts +614 -0
- package/src/createRenderer.ts +57 -0
- package/src/createStorage.ts +58 -0
- package/src/defineGenerator.ts +88 -100
- package/src/defineLogger.ts +13 -3
- package/src/defineParser.ts +45 -0
- package/src/definePlugin.ts +68 -7
- package/src/defineResolver.ts +501 -0
- package/src/devtools.ts +14 -14
- package/src/index.ts +12 -17
- package/src/mocks.ts +171 -0
- package/src/renderNode.ts +35 -0
- package/src/storages/fsStorage.ts +40 -11
- package/src/storages/memoryStorage.ts +4 -3
- package/src/types.ts +575 -205
- package/src/utils/TreeNode.ts +47 -9
- package/src/utils/diagnostics.ts +4 -1
- package/src/utils/getBarrelFiles.ts +94 -16
- package/src/utils/isInputPath.ts +10 -0
- package/src/utils/packageJSON.ts +99 -0
- package/dist/PluginManager-vZodFEMe.d.ts +0 -1056
- package/dist/chunk-ByKO4r7w.cjs +0 -38
- package/dist/hooks.cjs +0 -60
- package/dist/hooks.cjs.map +0 -1
- package/dist/hooks.d.ts +0 -56
- package/dist/hooks.js +0 -56
- package/dist/hooks.js.map +0 -1
- package/src/BarrelManager.ts +0 -74
- package/src/PackageManager.ts +0 -180
- package/src/PluginManager.ts +0 -667
- package/src/PromiseManager.ts +0 -40
- package/src/build.ts +0 -419
- package/src/config.ts +0 -56
- package/src/defineAdapter.ts +0 -22
- package/src/defineStorage.ts +0 -56
- package/src/errors.ts +0 -1
- package/src/hooks/index.ts +0 -4
- package/src/hooks/useKubb.ts +0 -46
- package/src/hooks/useMode.ts +0 -11
- package/src/hooks/usePlugin.ts +0 -11
- package/src/hooks/usePluginManager.ts +0 -11
- package/src/utils/FunctionParams.ts +0 -155
- package/src/utils/executeStrategies.ts +0 -81
- package/src/utils/formatters.ts +0 -56
- package/src/utils/getConfigs.ts +0 -30
- package/src/utils/getPlugins.ts +0 -23
- package/src/utils/linters.ts +0 -25
- package/src/utils/resolveOptions.ts +0 -93
package/src/utils/TreeNode.ts
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
2
|
+
import type { FileNode } from '@kubb/ast'
|
|
3
|
+
import { PluginDriver } from '../PluginDriver.ts'
|
|
4
4
|
|
|
5
5
|
type BarrelData = {
|
|
6
|
-
file?:
|
|
6
|
+
file?: FileNode
|
|
7
7
|
/**
|
|
8
8
|
* @deprecated use file instead
|
|
9
9
|
*/
|
|
10
|
-
type:
|
|
10
|
+
type: 'single' | 'split'
|
|
11
11
|
path: string
|
|
12
12
|
name: string
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Tree structure used to build per-directory barrel (`index.ts`) files from a
|
|
17
|
+
* flat list of generated {@link FileNode} entries.
|
|
18
|
+
*
|
|
19
|
+
* Each node represents either a directory or a file within the output tree.
|
|
20
|
+
* Use {@link TreeNode.build} to construct a root node from a file list, then
|
|
21
|
+
* traverse with {@link TreeNode.forEach}, {@link TreeNode.leaves}, or the
|
|
22
|
+
* `*Deep` helpers.
|
|
23
|
+
*/
|
|
15
24
|
export class TreeNode {
|
|
16
25
|
data: BarrelData
|
|
17
26
|
parent?: TreeNode
|
|
@@ -32,6 +41,9 @@ export class TreeNode {
|
|
|
32
41
|
return child
|
|
33
42
|
}
|
|
34
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Returns the root ancestor of this node, walking up via `parent` links.
|
|
46
|
+
*/
|
|
35
47
|
get root(): TreeNode {
|
|
36
48
|
if (!this.parent) {
|
|
37
49
|
return this
|
|
@@ -39,6 +51,11 @@ export class TreeNode {
|
|
|
39
51
|
return this.parent.root
|
|
40
52
|
}
|
|
41
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Returns all leaf descendants (nodes with no children) of this node.
|
|
56
|
+
*
|
|
57
|
+
* Results are cached after the first traversal.
|
|
58
|
+
*/
|
|
42
59
|
get leaves(): Array<TreeNode> {
|
|
43
60
|
if (!this.children || this.children.length === 0) {
|
|
44
61
|
// this is a leaf
|
|
@@ -59,6 +76,9 @@ export class TreeNode {
|
|
|
59
76
|
return leaves
|
|
60
77
|
}
|
|
61
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Visits this node and every descendant in depth-first order.
|
|
81
|
+
*/
|
|
62
82
|
forEach(callback: (treeNode: TreeNode) => void): this {
|
|
63
83
|
if (typeof callback !== 'function') {
|
|
64
84
|
throw new TypeError('forEach() callback must be a function')
|
|
@@ -73,6 +93,9 @@ export class TreeNode {
|
|
|
73
93
|
return this
|
|
74
94
|
}
|
|
75
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Finds the first leaf that satisfies `predicate`, or `undefined` when none match.
|
|
98
|
+
*/
|
|
76
99
|
findDeep(predicate?: (value: TreeNode, index: number, obj: TreeNode[]) => boolean): TreeNode | undefined {
|
|
77
100
|
if (typeof predicate !== 'function') {
|
|
78
101
|
throw new TypeError('find() predicate must be a function')
|
|
@@ -81,6 +104,9 @@ export class TreeNode {
|
|
|
81
104
|
return this.leaves.find(predicate)
|
|
82
105
|
}
|
|
83
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Calls `callback` for every leaf of this node.
|
|
109
|
+
*/
|
|
84
110
|
forEachDeep(callback: (treeNode: TreeNode) => void): void {
|
|
85
111
|
if (typeof callback !== 'function') {
|
|
86
112
|
throw new TypeError('forEach() callback must be a function')
|
|
@@ -89,6 +115,9 @@ export class TreeNode {
|
|
|
89
115
|
this.leaves.forEach(callback)
|
|
90
116
|
}
|
|
91
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Returns all leaves that satisfy `callback`.
|
|
120
|
+
*/
|
|
92
121
|
filterDeep(callback: (treeNode: TreeNode) => boolean): Array<TreeNode> {
|
|
93
122
|
if (typeof callback !== 'function') {
|
|
94
123
|
throw new TypeError('filter() callback must be a function')
|
|
@@ -97,6 +126,9 @@ export class TreeNode {
|
|
|
97
126
|
return this.leaves.filter(callback)
|
|
98
127
|
}
|
|
99
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Maps every leaf through `callback` and returns the resulting array.
|
|
131
|
+
*/
|
|
100
132
|
mapDeep<T>(callback: (treeNode: TreeNode) => T): Array<T> {
|
|
101
133
|
if (typeof callback !== 'function') {
|
|
102
134
|
throw new TypeError('map() callback must be a function')
|
|
@@ -105,7 +137,13 @@ export class TreeNode {
|
|
|
105
137
|
return this.leaves.map(callback)
|
|
106
138
|
}
|
|
107
139
|
|
|
108
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Builds a {@link TreeNode} tree from a flat list of files.
|
|
142
|
+
*
|
|
143
|
+
* - Filters to files under `root` (when provided) and skips `.json` files.
|
|
144
|
+
* - Returns `null` when no files match.
|
|
145
|
+
*/
|
|
146
|
+
public static build(files: FileNode[], root?: string): TreeNode | null {
|
|
109
147
|
try {
|
|
110
148
|
const filteredTree = buildDirectoryTree(files, root)
|
|
111
149
|
|
|
@@ -117,7 +155,7 @@ export class TreeNode {
|
|
|
117
155
|
name: filteredTree.name,
|
|
118
156
|
path: filteredTree.path,
|
|
119
157
|
file: filteredTree.file,
|
|
120
|
-
type: getMode(filteredTree.path),
|
|
158
|
+
type: PluginDriver.getMode(filteredTree.path),
|
|
121
159
|
})
|
|
122
160
|
|
|
123
161
|
const recurse = (node: typeof treeNode, item: DirectoryTree) => {
|
|
@@ -125,7 +163,7 @@ export class TreeNode {
|
|
|
125
163
|
name: item.name,
|
|
126
164
|
path: item.path,
|
|
127
165
|
file: item.file,
|
|
128
|
-
type: getMode(item.path),
|
|
166
|
+
type: PluginDriver.getMode(item.path),
|
|
129
167
|
})
|
|
130
168
|
|
|
131
169
|
if (item.children?.length) {
|
|
@@ -149,13 +187,13 @@ export class TreeNode {
|
|
|
149
187
|
type DirectoryTree = {
|
|
150
188
|
name: string
|
|
151
189
|
path: string
|
|
152
|
-
file?:
|
|
190
|
+
file?: FileNode
|
|
153
191
|
children: Array<DirectoryTree>
|
|
154
192
|
}
|
|
155
193
|
|
|
156
194
|
const normalizePath = (p: string): string => p.replaceAll('\\', '/')
|
|
157
195
|
|
|
158
|
-
function buildDirectoryTree(files: Array<
|
|
196
|
+
function buildDirectoryTree(files: Array<FileNode>, rootFolder = ''): DirectoryTree | null {
|
|
159
197
|
const normalizedRootFolder = normalizePath(rootFolder)
|
|
160
198
|
const rootPrefix = normalizedRootFolder.endsWith('/') ? normalizedRootFolder : `${normalizedRootFolder}/`
|
|
161
199
|
|
package/src/utils/diagnostics.ts
CHANGED
|
@@ -2,7 +2,10 @@ import { version as nodeVersion } from 'node:process'
|
|
|
2
2
|
import { version as KubbVersion } from '../../package.json'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Returns a snapshot of the current runtime environment.
|
|
6
|
+
*
|
|
7
|
+
* Useful for attaching context to debug logs and error reports so that
|
|
8
|
+
* issues can be reproduced without manual information gathering.
|
|
6
9
|
*/
|
|
7
10
|
export function getDiagnosticInfo() {
|
|
8
11
|
return {
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import { getRelativePath } from '@internals/utils'
|
|
3
|
+
import type { FileNode } from '@kubb/ast'
|
|
4
|
+
import { createExport, createFile, createSource } from '@kubb/ast'
|
|
5
|
+
import { BARREL_BASENAME, BARREL_FILENAME } from '../constants.ts'
|
|
4
6
|
import type { BarrelType } from '../types.ts'
|
|
7
|
+
import { TreeNode } from './TreeNode.ts'
|
|
5
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Minimal file metadata attached to every generated file for barrel-file bookkeeping.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
6
14
|
export type FileMetaBase = {
|
|
7
15
|
pluginName?: string
|
|
8
16
|
}
|
|
@@ -10,11 +18,11 @@ export type FileMetaBase = {
|
|
|
10
18
|
type AddIndexesProps = {
|
|
11
19
|
type: BarrelType | false | undefined
|
|
12
20
|
/**
|
|
13
|
-
*
|
|
21
|
+
* Absolute output root derived from config `root` and `output.path`.
|
|
14
22
|
*/
|
|
15
23
|
root: string
|
|
16
24
|
/**
|
|
17
|
-
* Output for plugin
|
|
25
|
+
* Output settings for the plugin.
|
|
18
26
|
*/
|
|
19
27
|
output: {
|
|
20
28
|
path: string
|
|
@@ -27,6 +35,74 @@ type AddIndexesProps = {
|
|
|
27
35
|
meta?: FileMetaBase
|
|
28
36
|
}
|
|
29
37
|
|
|
38
|
+
function getBarrelFilesByRoot(root: string | undefined, files: Array<FileNode>): Array<FileNode> {
|
|
39
|
+
const cachedFiles = new Map<string, FileNode>()
|
|
40
|
+
|
|
41
|
+
TreeNode.build(files, root)?.forEach((treeNode) => {
|
|
42
|
+
if (!treeNode?.children || !treeNode.parent?.data.path) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const barrelFilePath = join(treeNode.parent?.data.path, BARREL_FILENAME)
|
|
47
|
+
const barrelFile = createFile({
|
|
48
|
+
path: barrelFilePath,
|
|
49
|
+
baseName: BARREL_FILENAME,
|
|
50
|
+
exports: [],
|
|
51
|
+
imports: [],
|
|
52
|
+
sources: [],
|
|
53
|
+
})
|
|
54
|
+
const previousBarrelFile = cachedFiles.get(barrelFile.path)
|
|
55
|
+
const leaves = treeNode.leaves
|
|
56
|
+
|
|
57
|
+
leaves.forEach((item) => {
|
|
58
|
+
if (!item.data.name) {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const sources = item.data.file?.sources || []
|
|
63
|
+
|
|
64
|
+
sources.forEach((source) => {
|
|
65
|
+
if (!item.data.file?.path || !source.isIndexable || !source.name) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
const alreadyContainInPreviousBarrelFile = previousBarrelFile?.sources.some(
|
|
69
|
+
(item) => item.name === source.name && item.isTypeOnly === source.isTypeOnly,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if (alreadyContainInPreviousBarrelFile) {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
barrelFile.exports.push(
|
|
77
|
+
createExport({
|
|
78
|
+
name: [source.name],
|
|
79
|
+
path: getRelativePath(treeNode.parent?.data.path, item.data.path),
|
|
80
|
+
isTypeOnly: source.isTypeOnly,
|
|
81
|
+
}),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
barrelFile.sources.push(
|
|
85
|
+
createSource({
|
|
86
|
+
name: source.name,
|
|
87
|
+
isTypeOnly: source.isTypeOnly,
|
|
88
|
+
isExportable: false,
|
|
89
|
+
isIndexable: false,
|
|
90
|
+
}),
|
|
91
|
+
)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
if (previousBarrelFile) {
|
|
96
|
+
previousBarrelFile.sources.push(...barrelFile.sources)
|
|
97
|
+
previousBarrelFile.exports.push(...barrelFile.exports)
|
|
98
|
+
} else {
|
|
99
|
+
cachedFiles.set(barrelFile.path, barrelFile)
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
return [...cachedFiles.values()]
|
|
104
|
+
}
|
|
105
|
+
|
|
30
106
|
function trimExtName(text: string): string {
|
|
31
107
|
const dotIndex = text.lastIndexOf('.')
|
|
32
108
|
// Only strip when the dot is found and no path separator follows it
|
|
@@ -37,36 +113,38 @@ function trimExtName(text: string): string {
|
|
|
37
113
|
return text
|
|
38
114
|
}
|
|
39
115
|
|
|
40
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Generates `index.ts` barrel files for all directories under `root/output.path`.
|
|
118
|
+
*
|
|
119
|
+
* - Returns an empty array when `type` is falsy or `'propagate'`.
|
|
120
|
+
* - Skips generation when the output path itself ends with `index` (already a barrel).
|
|
121
|
+
* - When `type` is `'all'`, strips named exports so every re-export becomes a wildcard (`export * from`).
|
|
122
|
+
* - Attaches `meta` to each barrel file for downstream plugin identification.
|
|
123
|
+
*/
|
|
124
|
+
export async function getBarrelFiles(files: Array<FileNode>, { type, meta = {}, root, output }: AddIndexesProps): Promise<Array<FileNode>> {
|
|
41
125
|
if (!type || type === 'propagate') {
|
|
42
126
|
return []
|
|
43
127
|
}
|
|
44
128
|
|
|
45
|
-
const barrelManager = new BarrelManager()
|
|
46
|
-
|
|
47
129
|
const pathToBuildFrom = join(root, output.path)
|
|
48
130
|
|
|
49
|
-
if (trimExtName(pathToBuildFrom).endsWith(
|
|
131
|
+
if (trimExtName(pathToBuildFrom).endsWith(BARREL_BASENAME)) {
|
|
50
132
|
return []
|
|
51
133
|
}
|
|
52
134
|
|
|
53
|
-
const barrelFiles =
|
|
54
|
-
files,
|
|
55
|
-
root: pathToBuildFrom,
|
|
56
|
-
meta,
|
|
57
|
-
})
|
|
135
|
+
const barrelFiles = getBarrelFilesByRoot(pathToBuildFrom, files)
|
|
58
136
|
|
|
59
137
|
if (type === 'all') {
|
|
60
138
|
return barrelFiles.map((file) => {
|
|
61
139
|
return {
|
|
62
140
|
...file,
|
|
63
|
-
exports: file.exports
|
|
141
|
+
exports: file.exports.map((exportItem) => {
|
|
64
142
|
return {
|
|
65
143
|
...exportItem,
|
|
66
144
|
name: undefined,
|
|
67
145
|
}
|
|
68
146
|
}),
|
|
69
|
-
}
|
|
147
|
+
} as FileNode
|
|
70
148
|
})
|
|
71
149
|
}
|
|
72
150
|
|
|
@@ -74,6 +152,6 @@ export async function getBarrelFiles(files: Array<KubbFile.ResolvedFile>, { type
|
|
|
74
152
|
return {
|
|
75
153
|
...indexFile,
|
|
76
154
|
meta,
|
|
77
|
-
}
|
|
155
|
+
} as FileNode
|
|
78
156
|
})
|
|
79
157
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Config, InputPath, UserConfig } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type guard to check if a given config has an `input.path`.
|
|
5
|
+
*/
|
|
6
|
+
export function isInputPath(config: UserConfig | undefined): config is UserConfig<InputPath>
|
|
7
|
+
export function isInputPath(config: Config | undefined): config is Config<InputPath>
|
|
8
|
+
export function isInputPath(config: Config | UserConfig | undefined): config is Config<InputPath> | UserConfig<InputPath> {
|
|
9
|
+
return typeof config?.input === 'object' && config.input !== null && 'path' in config.input
|
|
10
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { findPackageJSON, readSync } from '@internals/utils'
|
|
2
|
+
|
|
3
|
+
type PackageJSON = {
|
|
4
|
+
dependencies?: Record<string, string>
|
|
5
|
+
devDependencies?: Record<string, string>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type DependencyName = string
|
|
9
|
+
type DependencyVersion = string
|
|
10
|
+
|
|
11
|
+
function getPackageJSONSync(cwd?: string): PackageJSON | null {
|
|
12
|
+
const pkgPath = findPackageJSON(cwd)
|
|
13
|
+
if (!pkgPath) {
|
|
14
|
+
return null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return JSON.parse(readSync(pkgPath)) as PackageJSON
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function match(packageJSON: PackageJSON, dependency: DependencyName | RegExp): string | null {
|
|
21
|
+
const dependencies = {
|
|
22
|
+
...(packageJSON.dependencies || {}),
|
|
23
|
+
...(packageJSON.devDependencies || {}),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (typeof dependency === 'string' && dependencies[dependency]) {
|
|
27
|
+
return dependencies[dependency]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const matched = Object.keys(dependencies).find((dep) => dep.match(dependency))
|
|
31
|
+
|
|
32
|
+
return matched ? (dependencies[matched] ?? null) : null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getVersionSync(dependency: DependencyName | RegExp, cwd?: string): DependencyVersion | null {
|
|
36
|
+
const packageJSON = getPackageJSONSync(cwd)
|
|
37
|
+
|
|
38
|
+
return packageJSON ? match(packageJSON, dependency) : null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns `true` when the nearest `package.json` declares a dependency that
|
|
43
|
+
* satisfies the given semver range.
|
|
44
|
+
*
|
|
45
|
+
* - Searches both `dependencies` and `devDependencies`.
|
|
46
|
+
* - Accepts a string package name or a `RegExp` to match scoped/pattern packages.
|
|
47
|
+
* - Uses `semver.satisfies` for range comparison; returns `false` when the
|
|
48
|
+
* version string cannot be coerced into a valid semver.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* satisfiesDependency('react', '>=18') // true when react@18.x is installed
|
|
53
|
+
* satisfiesDependency(/^@tanstack\//, '>=5') // true when any @tanstack/* >=5 is found
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
function coerceSemver(version: string): [number, number, number] | null {
|
|
57
|
+
const m = version.match(/(\d+)(?:\.(\d+))?(?:\.(\d+))?/)
|
|
58
|
+
return m ? [Number(m[1]), Number(m[2] ?? 0), Number(m[3] ?? 0)] : null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function satisfiesSemver(v: [number, number, number], range: string): boolean {
|
|
62
|
+
return range
|
|
63
|
+
.trim()
|
|
64
|
+
.split(/\s+/)
|
|
65
|
+
.every((cond) => {
|
|
66
|
+
const m = cond.match(/^(>=|<=|>|<|=|\^|~)?(\d+)(?:\.(\d+))?(?:\.(\d+))?$/)
|
|
67
|
+
if (!m) return false
|
|
68
|
+
const op = m[1] ?? '='
|
|
69
|
+
const r: [number, number, number] = [Number(m[2]), Number(m[3] ?? 0), Number(m[4] ?? 0)]
|
|
70
|
+
const cmp = v[0] !== r[0] ? v[0] - r[0] : v[1] !== r[1] ? v[1] - r[1] : v[2] - r[2]
|
|
71
|
+
if (op === '>=') return cmp >= 0
|
|
72
|
+
if (op === '<=') return cmp <= 0
|
|
73
|
+
if (op === '>') return cmp > 0
|
|
74
|
+
if (op === '<') return cmp < 0
|
|
75
|
+
if (op === '^') return v[0] === r[0] && cmp >= 0
|
|
76
|
+
if (op === '~') return v[0] === r[0] && v[1] === r[1] && cmp >= 0
|
|
77
|
+
return cmp === 0
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function satisfiesDependency(dependency: DependencyName | RegExp, version: DependencyVersion, cwd?: string): boolean {
|
|
82
|
+
const packageVersion = getVersionSync(dependency, cwd)
|
|
83
|
+
|
|
84
|
+
if (!packageVersion) {
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (packageVersion === version) {
|
|
89
|
+
return true
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const semVer = coerceSemver(packageVersion)
|
|
93
|
+
|
|
94
|
+
if (!semVer) {
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return satisfiesSemver(semVer, version)
|
|
99
|
+
}
|