flatlock 1.0.1 → 1.2.0
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 +55 -2
- package/bin/flatlock-cmp.js +109 -356
- package/dist/compare.d.ts +85 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/detect.d.ts +33 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/index.d.ts +60 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/parsers/index.d.ts +5 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/npm.d.ts +109 -0
- package/dist/parsers/npm.d.ts.map +1 -0
- package/dist/parsers/pnpm/detect.d.ts +136 -0
- package/dist/parsers/pnpm/detect.d.ts.map +1 -0
- package/dist/parsers/pnpm/index.d.ts +120 -0
- package/dist/parsers/pnpm/index.d.ts.map +1 -0
- package/dist/parsers/pnpm/internal.d.ts +5 -0
- package/dist/parsers/pnpm/internal.d.ts.map +1 -0
- package/dist/parsers/pnpm/shrinkwrap.d.ts +129 -0
- package/dist/parsers/pnpm/shrinkwrap.d.ts.map +1 -0
- package/dist/parsers/pnpm/v5.d.ts +139 -0
- package/dist/parsers/pnpm/v5.d.ts.map +1 -0
- package/dist/parsers/pnpm/v6plus.d.ts +212 -0
- package/dist/parsers/pnpm/v6plus.d.ts.map +1 -0
- package/dist/parsers/pnpm.d.ts +2 -0
- package/dist/parsers/pnpm.d.ts.map +1 -0
- package/dist/parsers/types.d.ts +23 -0
- package/dist/parsers/types.d.ts.map +1 -0
- package/dist/parsers/yarn-berry.d.ts +154 -0
- package/dist/parsers/yarn-berry.d.ts.map +1 -0
- package/dist/parsers/yarn-classic.d.ts +110 -0
- package/dist/parsers/yarn-classic.d.ts.map +1 -0
- package/dist/result.d.ts +12 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/set.d.ts +189 -0
- package/dist/set.d.ts.map +1 -0
- package/package.json +18 -7
- package/src/compare.js +620 -0
- package/src/detect.js +8 -7
- package/src/index.js +33 -15
- package/src/parsers/index.js +12 -4
- package/src/parsers/npm.js +70 -23
- package/src/parsers/pnpm/detect.js +198 -0
- package/src/parsers/pnpm/index.js +289 -0
- package/src/parsers/pnpm/internal.js +41 -0
- package/src/parsers/pnpm/shrinkwrap.js +241 -0
- package/src/parsers/pnpm/v5.js +225 -0
- package/src/parsers/pnpm/v6plus.js +290 -0
- package/src/parsers/pnpm.js +12 -77
- package/src/parsers/types.js +10 -0
- package/src/parsers/yarn-berry.js +187 -38
- package/src/parsers/yarn-classic.js +85 -24
- package/src/result.js +2 -2
- package/src/set.js +618 -0
- package/src/types.d.ts +54 -0
package/src/index.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import {
|
|
3
|
-
import { Ok, Err } from './result.js';
|
|
2
|
+
import { detectType, Type } from './detect.js';
|
|
4
3
|
import {
|
|
5
4
|
fromPackageLock,
|
|
6
5
|
fromPnpmLock,
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
fromYarnBerryLock,
|
|
7
|
+
fromYarnClassicLock
|
|
9
8
|
} from './parsers/index.js';
|
|
9
|
+
import { Err, Ok } from './result.js';
|
|
10
|
+
import { FlatlockSet } from './set.js';
|
|
11
|
+
|
|
12
|
+
/** @typedef {import('./detect.js').LockfileType} LockfileType */
|
|
13
|
+
/** @typedef {import('./parsers/npm.js').Dependency} Dependency */
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
|
-
* @typedef {
|
|
13
|
-
* @
|
|
14
|
-
* @
|
|
16
|
+
* @typedef {Object} ParseOptions
|
|
17
|
+
* @property {string} [path] - Path hint for type detection
|
|
18
|
+
* @property {LockfileType} [type] - Explicit type (skip detection)
|
|
15
19
|
*/
|
|
16
20
|
|
|
17
21
|
// Re-export Type and detection
|
|
@@ -23,6 +27,9 @@ export { Ok, Err };
|
|
|
23
27
|
// Re-export individual parsers
|
|
24
28
|
export { fromPackageLock, fromPnpmLock, fromYarnClassicLock, fromYarnBerryLock };
|
|
25
29
|
|
|
30
|
+
// Re-export FlatlockSet class
|
|
31
|
+
export { FlatlockSet };
|
|
32
|
+
|
|
26
33
|
/**
|
|
27
34
|
* Parse lockfile from path (auto-detect type)
|
|
28
35
|
* @param {string} path - Path to lockfile
|
|
@@ -73,23 +80,23 @@ export function* fromString(content, options = {}) {
|
|
|
73
80
|
/**
|
|
74
81
|
* Try to parse lockfile from path (returns Result)
|
|
75
82
|
* @param {string} path - Path to lockfile
|
|
76
|
-
* @param {
|
|
77
|
-
* @returns {Promise<Result<AsyncGenerator<Dependency>>>}
|
|
83
|
+
* @param {ParseOptions} [options] - Parser options
|
|
84
|
+
* @returns {Promise<import('./result.js').Result<AsyncGenerator<Dependency>>>}
|
|
78
85
|
*/
|
|
79
86
|
export async function tryFromPath(path, options = {}) {
|
|
80
87
|
try {
|
|
81
88
|
const generator = fromPath(path, options);
|
|
82
89
|
return Ok(generator);
|
|
83
90
|
} catch (err) {
|
|
84
|
-
return Err(err);
|
|
91
|
+
return Err(/** @type {Error} */ (err));
|
|
85
92
|
}
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
/**
|
|
89
96
|
* Try to parse lockfile from string (returns Result)
|
|
90
97
|
* @param {string} content - Lockfile content
|
|
91
|
-
* @param {
|
|
92
|
-
* @returns {Result<Generator<Dependency>>}
|
|
98
|
+
* @param {ParseOptions} [options] - Parser options
|
|
99
|
+
* @returns {import('./result.js').Result<Generator<Dependency>>}
|
|
93
100
|
*/
|
|
94
101
|
export function tryFromString(content, options = {}) {
|
|
95
102
|
try {
|
|
@@ -98,7 +105,7 @@ export function tryFromString(content, options = {}) {
|
|
|
98
105
|
const generator = fromString(content, { ...options, type });
|
|
99
106
|
return Ok(generator);
|
|
100
107
|
} catch (err) {
|
|
101
|
-
return Err(err);
|
|
108
|
+
return Err(/** @type {Error} */ (err));
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
111
|
|
|
@@ -127,8 +134,8 @@ export function* fromYarnLock(content, options = {}) {
|
|
|
127
134
|
export async function collect(pathOrContent, options = {}) {
|
|
128
135
|
const deps = [];
|
|
129
136
|
|
|
130
|
-
//
|
|
131
|
-
const isPath = !pathOrContent.includes('\n') &&
|
|
137
|
+
// Better heuristic: paths don't contain newlines and are reasonably short
|
|
138
|
+
const isPath = !pathOrContent.includes('\n') && pathOrContent.length < 1000;
|
|
132
139
|
|
|
133
140
|
if (isPath) {
|
|
134
141
|
for await (const dep of fromPath(pathOrContent, options)) {
|
|
@@ -142,3 +149,14 @@ export async function collect(pathOrContent, options = {}) {
|
|
|
142
149
|
|
|
143
150
|
return deps;
|
|
144
151
|
}
|
|
152
|
+
|
|
153
|
+
// Re-export compare API
|
|
154
|
+
export { compare, compareAll, getAvailableParsers } from './compare.js';
|
|
155
|
+
|
|
156
|
+
// Re-export lockfile key parsing utilities
|
|
157
|
+
export {
|
|
158
|
+
parseNpmKey,
|
|
159
|
+
parsePnpmKey,
|
|
160
|
+
parseYarnBerryKey,
|
|
161
|
+
parseYarnClassicKey
|
|
162
|
+
} from './parsers/index.js';
|
package/src/parsers/index.js
CHANGED
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
* Re-export all lockfile parsers
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export { fromPackageLock } from './npm.js';
|
|
6
|
-
export { fromPnpmLock } from './pnpm.js';
|
|
7
|
-
export {
|
|
8
|
-
|
|
5
|
+
export { fromPackageLock, parseLockfileKey as parseNpmKey } from './npm.js';
|
|
6
|
+
export { fromPnpmLock, parseLockfileKey as parsePnpmKey } from './pnpm.js';
|
|
7
|
+
export {
|
|
8
|
+
fromYarnBerryLock,
|
|
9
|
+
parseLockfileKey as parseYarnBerryKey,
|
|
10
|
+
parseResolution as parseYarnBerryResolution
|
|
11
|
+
} from './yarn-berry.js';
|
|
12
|
+
export {
|
|
13
|
+
fromYarnClassicLock,
|
|
14
|
+
parseLockfileKey as parseYarnClassicKey,
|
|
15
|
+
parseYarnClassic
|
|
16
|
+
} from './yarn-classic.js';
|
package/src/parsers/npm.js
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} Dependency
|
|
3
|
-
* @property {string} name - Package name
|
|
4
|
-
* @property {string} version - Resolved version
|
|
5
|
-
* @property {string} [integrity] - Integrity hash
|
|
6
|
-
* @property {string} [resolved] - Resolution URL
|
|
7
|
-
* @property {boolean} [link] - True if this is a symlink
|
|
8
|
-
*/
|
|
1
|
+
/** @typedef {import('./types.js').Dependency} Dependency */
|
|
9
2
|
|
|
10
3
|
/**
|
|
11
4
|
* LIMITATION: Workspace symlinks are not yielded
|
|
@@ -43,32 +36,85 @@
|
|
|
43
36
|
* pkg := name (unscoped)
|
|
44
37
|
* | @scope/name (scoped)
|
|
45
38
|
*
|
|
46
|
-
* Examples:
|
|
47
|
-
* - node_modules/lodash → "lodash"
|
|
48
|
-
* - node_modules/@babel/core → "@babel/core"
|
|
49
|
-
* - node_modules/foo/node_modules/@scope/bar → "@scope/bar"
|
|
50
|
-
*
|
|
51
39
|
* @param {string} path - Lockfile path key
|
|
52
40
|
* @returns {string} Package name
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* // Simple unscoped package
|
|
44
|
+
* parseLockfileKey('node_modules/lodash')
|
|
45
|
+
* // => 'lodash'
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // Scoped package
|
|
49
|
+
* parseLockfileKey('node_modules/@babel/core')
|
|
50
|
+
* // => '@babel/core'
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // Nested dependency (hoisted conflict resolution)
|
|
54
|
+
* parseLockfileKey('node_modules/foo/node_modules/bar')
|
|
55
|
+
* // => 'bar'
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* // Nested scoped dependency
|
|
59
|
+
* parseLockfileKey('node_modules/foo/node_modules/@scope/bar')
|
|
60
|
+
* // => '@scope/bar'
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* // Deeply nested dependency
|
|
64
|
+
* parseLockfileKey('node_modules/a/node_modules/b/node_modules/c')
|
|
65
|
+
* // => 'c'
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* // Deeply nested scoped dependency
|
|
69
|
+
* parseLockfileKey('node_modules/a/node_modules/@types/node')
|
|
70
|
+
* // => '@types/node'
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* // Workspace package path (definition)
|
|
74
|
+
* parseLockfileKey('packages/my-lib')
|
|
75
|
+
* // => 'my-lib'
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* // Workspace nested dependency
|
|
79
|
+
* parseLockfileKey('packages/my-lib/node_modules/lodash')
|
|
80
|
+
* // => 'lodash'
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* // Workspace nested scoped dependency
|
|
84
|
+
* parseLockfileKey('packages/my-lib/node_modules/@types/react')
|
|
85
|
+
* // => '@types/react'
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* // Package with hyphenated name
|
|
89
|
+
* parseLockfileKey('node_modules/string-width')
|
|
90
|
+
* // => 'string-width'
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* // Scoped package with hyphenated name
|
|
94
|
+
* parseLockfileKey('node_modules/@emotion/styled')
|
|
95
|
+
* // => '@emotion/styled'
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* // Complex nested path
|
|
99
|
+
* parseLockfileKey('node_modules/@babel/core/node_modules/@babel/helper-compilation-targets')
|
|
100
|
+
* // => '@babel/helper-compilation-targets'
|
|
53
101
|
*/
|
|
54
|
-
function
|
|
102
|
+
export function parseLockfileKey(path) {
|
|
55
103
|
const parts = path.split('/');
|
|
56
|
-
const name = parts.at(-1);
|
|
104
|
+
const name = /** @type {string} */ (parts.at(-1));
|
|
57
105
|
const maybeScope = parts.at(-2);
|
|
58
106
|
|
|
59
|
-
return maybeScope?.startsWith('@')
|
|
60
|
-
? `${maybeScope}/${name}`
|
|
61
|
-
: name;
|
|
107
|
+
return maybeScope?.startsWith('@') ? `${maybeScope}/${name}` : name;
|
|
62
108
|
}
|
|
63
109
|
|
|
64
110
|
/**
|
|
65
111
|
* Parse npm package-lock.json (v1, v2, v3)
|
|
66
|
-
* @param {string}
|
|
67
|
-
* @param {Object} [
|
|
112
|
+
* @param {string | object} input - Lockfile content string or pre-parsed object
|
|
113
|
+
* @param {Object} [_options] - Parser options (unused, reserved for future use)
|
|
68
114
|
* @returns {Generator<Dependency>}
|
|
69
115
|
*/
|
|
70
|
-
export function* fromPackageLock(
|
|
71
|
-
const lockfile = JSON.parse(
|
|
116
|
+
export function* fromPackageLock(input, _options = {}) {
|
|
117
|
+
const lockfile = typeof input === 'string' ? JSON.parse(input) : input;
|
|
72
118
|
const packages = lockfile.packages || {};
|
|
73
119
|
|
|
74
120
|
for (const [path, pkg] of Object.entries(packages)) {
|
|
@@ -81,11 +127,12 @@ export function* fromPackageLock(content, _options = {}) {
|
|
|
81
127
|
// 2. node_modules/<workspace-package.json-name> → link, NO version (symlink)
|
|
82
128
|
if (!path.includes('node_modules/')) continue;
|
|
83
129
|
|
|
84
|
-
const name =
|
|
130
|
+
const name = parseLockfileKey(path);
|
|
85
131
|
const { version, integrity, resolved, link } = pkg;
|
|
86
132
|
|
|
87
133
|
// Only yield if we have a name and version
|
|
88
134
|
if (name && version) {
|
|
135
|
+
/** @type {Dependency} */
|
|
89
136
|
const dep = { name, version };
|
|
90
137
|
if (integrity) dep.integrity = integrity;
|
|
91
138
|
if (resolved) dep.resolved = resolved;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Version detection for pnpm lockfiles
|
|
3
|
+
*
|
|
4
|
+
* Detects the era and version of pnpm lockfiles including:
|
|
5
|
+
* - shrinkwrap.yaml (v3/v4) from 2016-2019
|
|
6
|
+
* - pnpm-lock.yaml v5.x (2019-2022)
|
|
7
|
+
* - pnpm-lock.yaml v6.0 (2023)
|
|
8
|
+
* - pnpm-lock.yaml v9.0 (2024+)
|
|
9
|
+
*
|
|
10
|
+
* @module flatlock/parsers/pnpm/detect
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {'shrinkwrap'|'v5'|'v5-inline'|'v6'|'v9'|'unknown'} LockfileEra
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} DetectedVersion
|
|
19
|
+
* @property {LockfileEra} era - The lockfile era/format family
|
|
20
|
+
* @property {string|number} version - The raw version from the lockfile
|
|
21
|
+
* @property {boolean} isShrinkwrap - True if this is a shrinkwrap.yaml file
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Detect the version and era of a pnpm lockfile.
|
|
26
|
+
*
|
|
27
|
+
* Version detection rules:
|
|
28
|
+
* - If `shrinkwrapVersion` exists: shrinkwrap era (v3/v4)
|
|
29
|
+
* - If `lockfileVersion` is a number: v5 era
|
|
30
|
+
* - If `lockfileVersion` is '5.4-inlineSpecifiers': v5-inline era
|
|
31
|
+
* - If `lockfileVersion` starts with '6': v6 era
|
|
32
|
+
* - If `lockfileVersion` starts with '9': v9 era
|
|
33
|
+
*
|
|
34
|
+
* @param {Object} lockfile - Parsed pnpm lockfile object
|
|
35
|
+
* @param {string|number} [lockfile.lockfileVersion] - The lockfile version field
|
|
36
|
+
* @param {number} [lockfile.shrinkwrapVersion] - The shrinkwrap version field (v3/v4)
|
|
37
|
+
* @param {number} [lockfile.shrinkwrapMinorVersion] - Minor version for shrinkwrap
|
|
38
|
+
* @returns {DetectedVersion} The detected version information
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // shrinkwrap.yaml v3
|
|
42
|
+
* detectVersion({ shrinkwrapVersion: 3 })
|
|
43
|
+
* // => { era: 'shrinkwrap', version: 3, isShrinkwrap: true }
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* // pnpm-lock.yaml v5.4
|
|
47
|
+
* detectVersion({ lockfileVersion: 5.4 })
|
|
48
|
+
* // => { era: 'v5', version: 5.4, isShrinkwrap: false }
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // pnpm-lock.yaml v6.0
|
|
52
|
+
* detectVersion({ lockfileVersion: '6.0' })
|
|
53
|
+
* // => { era: 'v6', version: '6.0', isShrinkwrap: false }
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // pnpm-lock.yaml v9.0
|
|
57
|
+
* detectVersion({ lockfileVersion: '9.0' })
|
|
58
|
+
* // => { era: 'v9', version: '9.0', isShrinkwrap: false }
|
|
59
|
+
*/
|
|
60
|
+
export function detectVersion(lockfile) {
|
|
61
|
+
// Handle null/undefined input
|
|
62
|
+
if (!lockfile || typeof lockfile !== 'object') {
|
|
63
|
+
return { era: 'unknown', version: '', isShrinkwrap: false };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check for shrinkwrap.yaml (v3/v4) - oldest format
|
|
67
|
+
if ('shrinkwrapVersion' in lockfile) {
|
|
68
|
+
const version = lockfile.shrinkwrapVersion;
|
|
69
|
+
return {
|
|
70
|
+
era: 'shrinkwrap',
|
|
71
|
+
version: version ?? 0,
|
|
72
|
+
isShrinkwrap: true
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Get the lockfileVersion
|
|
77
|
+
const version = lockfile.lockfileVersion;
|
|
78
|
+
|
|
79
|
+
// Handle missing version
|
|
80
|
+
if (version === undefined || version === null) {
|
|
81
|
+
return { era: 'unknown', version: '', isShrinkwrap: false };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Numeric version: v5.x era
|
|
85
|
+
if (typeof version === 'number') {
|
|
86
|
+
return {
|
|
87
|
+
era: 'v5',
|
|
88
|
+
version: version,
|
|
89
|
+
isShrinkwrap: false
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// String version
|
|
94
|
+
if (typeof version === 'string') {
|
|
95
|
+
// v5.4-inlineSpecifiers: experimental transitional format
|
|
96
|
+
if (version.includes('inlineSpecifiers')) {
|
|
97
|
+
return {
|
|
98
|
+
era: 'v5-inline',
|
|
99
|
+
version: version,
|
|
100
|
+
isShrinkwrap: false
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// v9.x era (2024+)
|
|
105
|
+
if (version.startsWith('9')) {
|
|
106
|
+
return {
|
|
107
|
+
era: 'v9',
|
|
108
|
+
version: version,
|
|
109
|
+
isShrinkwrap: false
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// v6.x era (2023)
|
|
114
|
+
if (version.startsWith('6')) {
|
|
115
|
+
return {
|
|
116
|
+
era: 'v6',
|
|
117
|
+
version: version,
|
|
118
|
+
isShrinkwrap: false
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// v7.x or v8.x would fall here if they existed (they don't)
|
|
123
|
+
// Could be a future version we don't know about
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { era: 'unknown', version: version, isShrinkwrap: false };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check if a lockfile uses the v6+ package key format (name@version).
|
|
131
|
+
*
|
|
132
|
+
* v5 and earlier use: /name/version or /@scope/name/version
|
|
133
|
+
* v6+ use: /name@version or /@scope/name@version
|
|
134
|
+
* v9+ use: name@version (no leading slash)
|
|
135
|
+
*
|
|
136
|
+
* @param {DetectedVersion} detected - The detected version info
|
|
137
|
+
* @returns {boolean} True if the lockfile uses @ separator for name@version
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* usesAtSeparator({ era: 'v5', version: 5.4 }) // => false
|
|
141
|
+
* usesAtSeparator({ era: 'v6', version: '6.0' }) // => true
|
|
142
|
+
* usesAtSeparator({ era: 'v9', version: '9.0' }) // => true
|
|
143
|
+
*/
|
|
144
|
+
export function usesAtSeparator(detected) {
|
|
145
|
+
return detected.era === 'v6' || detected.era === 'v9';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Check if a lockfile uses the packages/snapshots split (v9+).
|
|
150
|
+
*
|
|
151
|
+
* v9 separates package metadata (packages) from dependency relationships (snapshots).
|
|
152
|
+
*
|
|
153
|
+
* @param {DetectedVersion} detected - The detected version info
|
|
154
|
+
* @returns {boolean} True if the lockfile has packages/snapshots split
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* usesSnapshotsSplit({ era: 'v6', version: '6.0' }) // => false
|
|
158
|
+
* usesSnapshotsSplit({ era: 'v9', version: '9.0' }) // => true
|
|
159
|
+
*/
|
|
160
|
+
export function usesSnapshotsSplit(detected) {
|
|
161
|
+
return detected.era === 'v9';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Check if a lockfile uses inline specifiers.
|
|
166
|
+
*
|
|
167
|
+
* v5.4-inlineSpecifiers and v6+ use inline specifiers in importers.
|
|
168
|
+
* Earlier versions have a separate `specifiers` block.
|
|
169
|
+
*
|
|
170
|
+
* @param {DetectedVersion} detected - The detected version info
|
|
171
|
+
* @returns {boolean} True if specifiers are inlined
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* usesInlineSpecifiers({ era: 'v5', version: 5.4 }) // => false
|
|
175
|
+
* usesInlineSpecifiers({ era: 'v5-inline', version: '5.4-inlineSpecifiers' }) // => true
|
|
176
|
+
* usesInlineSpecifiers({ era: 'v6', version: '6.0' }) // => true
|
|
177
|
+
*/
|
|
178
|
+
export function usesInlineSpecifiers(detected) {
|
|
179
|
+
return detected.era === 'v5-inline' || detected.era === 'v6' || detected.era === 'v9';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Check if package keys have a leading slash.
|
|
184
|
+
*
|
|
185
|
+
* v5 and v6 use leading slash: /name/version or /name@version
|
|
186
|
+
* v9 omits leading slash: name@version
|
|
187
|
+
*
|
|
188
|
+
* @param {DetectedVersion} detected - The detected version info
|
|
189
|
+
* @returns {boolean} True if package keys have leading slash
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* hasLeadingSlash({ era: 'v5', version: 5.4 }) // => true
|
|
193
|
+
* hasLeadingSlash({ era: 'v6', version: '6.0' }) // => true
|
|
194
|
+
* hasLeadingSlash({ era: 'v9', version: '9.0' }) // => false
|
|
195
|
+
*/
|
|
196
|
+
export function hasLeadingSlash(detected) {
|
|
197
|
+
return detected.era !== 'v9';
|
|
198
|
+
}
|