flatlock 1.2.0 → 1.3.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 +42 -1
- package/bin/flatcover.js +398 -0
- package/bin/flatlock.js +158 -0
- package/package.json +16 -5
- package/src/parsers/index.js +14 -2
- package/src/parsers/npm.js +82 -0
- package/src/parsers/pnpm/index.js +70 -0
- package/src/parsers/yarn-berry.js +88 -0
- package/src/set.js +730 -41
- package/dist/compare.d.ts +0 -85
- package/dist/compare.d.ts.map +0 -1
- package/dist/detect.d.ts +0 -33
- package/dist/detect.d.ts.map +0 -1
- package/dist/index.d.ts +0 -72
- package/dist/index.d.ts.map +0 -1
- package/dist/parsers/index.d.ts +0 -5
- package/dist/parsers/index.d.ts.map +0 -1
- package/dist/parsers/npm.d.ts +0 -109
- package/dist/parsers/npm.d.ts.map +0 -1
- package/dist/parsers/pnpm/detect.d.ts +0 -136
- package/dist/parsers/pnpm/detect.d.ts.map +0 -1
- package/dist/parsers/pnpm/index.d.ts +0 -120
- package/dist/parsers/pnpm/index.d.ts.map +0 -1
- package/dist/parsers/pnpm/internal.d.ts +0 -5
- package/dist/parsers/pnpm/internal.d.ts.map +0 -1
- package/dist/parsers/pnpm/shrinkwrap.d.ts +0 -129
- package/dist/parsers/pnpm/shrinkwrap.d.ts.map +0 -1
- package/dist/parsers/pnpm/v5.d.ts +0 -139
- package/dist/parsers/pnpm/v5.d.ts.map +0 -1
- package/dist/parsers/pnpm/v6plus.d.ts +0 -212
- package/dist/parsers/pnpm/v6plus.d.ts.map +0 -1
- package/dist/parsers/pnpm.d.ts +0 -2
- package/dist/parsers/pnpm.d.ts.map +0 -1
- package/dist/parsers/types.d.ts +0 -23
- package/dist/parsers/types.d.ts.map +0 -1
- package/dist/parsers/yarn-berry.d.ts +0 -154
- package/dist/parsers/yarn-berry.d.ts.map +0 -1
- package/dist/parsers/yarn-classic.d.ts +0 -110
- package/dist/parsers/yarn-classic.d.ts.map +0 -1
- package/dist/result.d.ts +0 -12
- package/dist/result.d.ts.map +0 -1
- package/dist/set.d.ts +0 -189
- package/dist/set.d.ts.map +0 -1
package/src/parsers/npm.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
1
4
|
/** @typedef {import('./types.js').Dependency} Dependency */
|
|
2
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} WorkspacePackage
|
|
8
|
+
* @property {string} name
|
|
9
|
+
* @property {string} version
|
|
10
|
+
* @property {Record<string, string>} [dependencies]
|
|
11
|
+
* @property {Record<string, string>} [devDependencies]
|
|
12
|
+
* @property {Record<string, string>} [optionalDependencies]
|
|
13
|
+
* @property {Record<string, string>} [peerDependencies]
|
|
14
|
+
*/
|
|
15
|
+
|
|
3
16
|
/**
|
|
4
17
|
* LIMITATION: Workspace symlinks are not yielded
|
|
5
18
|
*
|
|
@@ -141,3 +154,72 @@ export function* fromPackageLock(input, _options = {}) {
|
|
|
141
154
|
}
|
|
142
155
|
}
|
|
143
156
|
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Extract workspace paths from npm lockfile.
|
|
160
|
+
*
|
|
161
|
+
* npm workspace packages are entries in `packages` that:
|
|
162
|
+
* - Are not the root ('')
|
|
163
|
+
* - Don't contain 'node_modules/' (those are installed deps)
|
|
164
|
+
* - Have a version field
|
|
165
|
+
*
|
|
166
|
+
* @param {string | object} input - Lockfile content string or pre-parsed object
|
|
167
|
+
* @returns {string[]} Array of workspace paths (e.g., ['workspaces/arborist', 'workspaces/libnpmfund'])
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* extractWorkspacePaths(lockfile)
|
|
171
|
+
* // => ['workspaces/arborist', 'workspaces/libnpmfund', ...]
|
|
172
|
+
*/
|
|
173
|
+
export function extractWorkspacePaths(input) {
|
|
174
|
+
const lockfile = typeof input === 'string' ? JSON.parse(input) : input;
|
|
175
|
+
const packages = lockfile.packages || {};
|
|
176
|
+
const paths = [];
|
|
177
|
+
|
|
178
|
+
for (const [path, pkg] of Object.entries(packages)) {
|
|
179
|
+
// Skip root and node_modules entries
|
|
180
|
+
if (path === '' || path.includes('node_modules')) continue;
|
|
181
|
+
|
|
182
|
+
// Workspace entries have a version
|
|
183
|
+
if (pkg.version) {
|
|
184
|
+
paths.push(path);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return paths;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Build workspace packages map by reading package.json files.
|
|
193
|
+
*
|
|
194
|
+
* @param {string | object} input - Lockfile content string or pre-parsed object
|
|
195
|
+
* @param {string} repoDir - Path to repository root
|
|
196
|
+
* @returns {Promise<Record<string, WorkspacePackage>>} Map of workspace path to package info
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* const workspaces = await buildWorkspacePackages(lockfile, '/path/to/repo');
|
|
200
|
+
* // => { 'workspaces/arborist': { name: '@npmcli/arborist', version: '1.0.0', dependencies: {...} } }
|
|
201
|
+
*/
|
|
202
|
+
export async function buildWorkspacePackages(input, repoDir) {
|
|
203
|
+
const paths = extractWorkspacePaths(input);
|
|
204
|
+
/** @type {Record<string, WorkspacePackage>} */
|
|
205
|
+
const workspacePackages = {};
|
|
206
|
+
|
|
207
|
+
for (const wsPath of paths) {
|
|
208
|
+
const pkgJsonPath = join(repoDir, wsPath, 'package.json');
|
|
209
|
+
try {
|
|
210
|
+
const pkg = JSON.parse(await readFile(pkgJsonPath, 'utf8'));
|
|
211
|
+
workspacePackages[wsPath] = {
|
|
212
|
+
name: pkg.name,
|
|
213
|
+
version: pkg.version || '0.0.0',
|
|
214
|
+
dependencies: pkg.dependencies,
|
|
215
|
+
devDependencies: pkg.devDependencies,
|
|
216
|
+
optionalDependencies: pkg.optionalDependencies,
|
|
217
|
+
peerDependencies: pkg.peerDependencies
|
|
218
|
+
};
|
|
219
|
+
} catch {
|
|
220
|
+
// Skip workspaces with missing or invalid package.json
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return workspacePackages;
|
|
225
|
+
}
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* @module flatlock/parsers/pnpm
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { readFile } from 'node:fs/promises';
|
|
15
|
+
import { join } from 'node:path';
|
|
14
16
|
import yaml from 'js-yaml';
|
|
15
17
|
|
|
16
18
|
import { detectVersion } from './detect.js';
|
|
@@ -20,6 +22,16 @@ import { parseSpecV6Plus } from './v6plus.js';
|
|
|
20
22
|
|
|
21
23
|
/** @typedef {import('../types.js').Dependency} Dependency */
|
|
22
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {Object} WorkspacePackage
|
|
27
|
+
* @property {string} name
|
|
28
|
+
* @property {string} version
|
|
29
|
+
* @property {Record<string, string>} [dependencies]
|
|
30
|
+
* @property {Record<string, string>} [devDependencies]
|
|
31
|
+
* @property {Record<string, string>} [optionalDependencies]
|
|
32
|
+
* @property {Record<string, string>} [peerDependencies]
|
|
33
|
+
*/
|
|
34
|
+
|
|
23
35
|
// Public API: detectVersion for users who need to inspect lockfile version
|
|
24
36
|
export { detectVersion } from './detect.js';
|
|
25
37
|
|
|
@@ -287,3 +299,61 @@ export function* fromPnpmLock(input, _options = {}) {
|
|
|
287
299
|
// Note: importers (workspace packages) are intentionally NOT yielded
|
|
288
300
|
// flatlock only cares about external dependencies
|
|
289
301
|
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Extract workspace paths from pnpm lockfile.
|
|
305
|
+
*
|
|
306
|
+
* pnpm stores workspace packages in the `importers` section.
|
|
307
|
+
* Each key is a workspace path relative to the repo root.
|
|
308
|
+
*
|
|
309
|
+
* @param {string | object} input - Lockfile content string or pre-parsed object
|
|
310
|
+
* @returns {string[]} Array of workspace paths (e.g., ['packages/foo', 'packages/bar'])
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* extractWorkspacePaths(lockfile)
|
|
314
|
+
* // => ['packages/vue', 'packages/compiler-core', ...]
|
|
315
|
+
*/
|
|
316
|
+
export function extractWorkspacePaths(input) {
|
|
317
|
+
const lockfile = /** @type {Record<string, any>} */ (
|
|
318
|
+
typeof input === 'string' ? yaml.load(input) : input
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
const importers = lockfile.importers || {};
|
|
322
|
+
return Object.keys(importers).filter(k => k !== '.');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Build workspace packages map by reading package.json files.
|
|
327
|
+
*
|
|
328
|
+
* @param {string | object} input - Lockfile content string or pre-parsed object
|
|
329
|
+
* @param {string} repoDir - Path to repository root
|
|
330
|
+
* @returns {Promise<Record<string, WorkspacePackage>>} Map of workspace path to package info
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* const workspaces = await buildWorkspacePackages(lockfile, '/path/to/repo');
|
|
334
|
+
* // => { 'packages/foo': { name: '@scope/foo', version: '1.0.0', dependencies: {...} } }
|
|
335
|
+
*/
|
|
336
|
+
export async function buildWorkspacePackages(input, repoDir) {
|
|
337
|
+
const paths = extractWorkspacePaths(input);
|
|
338
|
+
/** @type {Record<string, WorkspacePackage>} */
|
|
339
|
+
const workspacePackages = {};
|
|
340
|
+
|
|
341
|
+
for (const wsPath of paths) {
|
|
342
|
+
const pkgJsonPath = join(repoDir, wsPath, 'package.json');
|
|
343
|
+
try {
|
|
344
|
+
const pkg = JSON.parse(await readFile(pkgJsonPath, 'utf8'));
|
|
345
|
+
workspacePackages[wsPath] = {
|
|
346
|
+
name: pkg.name,
|
|
347
|
+
version: pkg.version || '0.0.0',
|
|
348
|
+
dependencies: pkg.dependencies,
|
|
349
|
+
devDependencies: pkg.devDependencies,
|
|
350
|
+
optionalDependencies: pkg.optionalDependencies,
|
|
351
|
+
peerDependencies: pkg.peerDependencies
|
|
352
|
+
};
|
|
353
|
+
} catch {
|
|
354
|
+
// Skip workspaces with missing or invalid package.json
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return workspacePackages;
|
|
359
|
+
}
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { parseSyml } from '@yarnpkg/parsers';
|
|
2
4
|
|
|
3
5
|
/** @typedef {import('./types.js').Dependency} Dependency */
|
|
4
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} WorkspacePackage
|
|
9
|
+
* @property {string} name
|
|
10
|
+
* @property {string} version
|
|
11
|
+
* @property {Record<string, string>} [dependencies]
|
|
12
|
+
* @property {Record<string, string>} [devDependencies]
|
|
13
|
+
* @property {Record<string, string>} [optionalDependencies]
|
|
14
|
+
* @property {Record<string, string>} [peerDependencies]
|
|
15
|
+
*/
|
|
16
|
+
|
|
5
17
|
/**
|
|
6
18
|
* Extract package name from yarn berry resolution field.
|
|
7
19
|
*
|
|
@@ -252,3 +264,79 @@ export function* fromYarnBerryLock(input, _options = {}) {
|
|
|
252
264
|
}
|
|
253
265
|
}
|
|
254
266
|
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Extract workspace paths from yarn berry lockfile.
|
|
270
|
+
*
|
|
271
|
+
* Yarn berry workspace entries use `@workspace:` protocol in keys.
|
|
272
|
+
* Keys can have multiple comma-separated descriptors.
|
|
273
|
+
*
|
|
274
|
+
* @param {string | object} input - Lockfile content string or pre-parsed object
|
|
275
|
+
* @returns {string[]} Array of workspace paths (e.g., ['packages/foo', 'packages/bar'])
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* extractWorkspacePaths(lockfile)
|
|
279
|
+
* // => ['packages/babel-core', 'packages/babel-parser', ...]
|
|
280
|
+
*/
|
|
281
|
+
export function extractWorkspacePaths(input) {
|
|
282
|
+
const lockfile = typeof input === 'string' ? parseSyml(input) : input;
|
|
283
|
+
const paths = new Set();
|
|
284
|
+
|
|
285
|
+
for (const key of Object.keys(lockfile)) {
|
|
286
|
+
if (key === '__metadata') continue;
|
|
287
|
+
if (!key.includes('@workspace:')) continue;
|
|
288
|
+
|
|
289
|
+
// Keys can have multiple comma-separated descriptors:
|
|
290
|
+
// "@babel/types@workspace:*, @babel/types@workspace:^, @babel/types@workspace:packages/babel-types"
|
|
291
|
+
const descriptors = key.split(', ');
|
|
292
|
+
for (const desc of descriptors) {
|
|
293
|
+
if (!desc.includes('@workspace:')) continue;
|
|
294
|
+
|
|
295
|
+
const wsIndex = desc.indexOf('@workspace:');
|
|
296
|
+
const path = desc.slice(wsIndex + '@workspace:'.length);
|
|
297
|
+
|
|
298
|
+
// Skip wildcards (*, ^) and root workspace (.)
|
|
299
|
+
if (path && path !== '.' && path !== '*' && path !== '^' && path.includes('/')) {
|
|
300
|
+
paths.add(path);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return [...paths];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Build workspace packages map by reading package.json files.
|
|
310
|
+
*
|
|
311
|
+
* @param {string | object} input - Lockfile content string or pre-parsed object
|
|
312
|
+
* @param {string} repoDir - Path to repository root
|
|
313
|
+
* @returns {Promise<Record<string, WorkspacePackage>>} Map of workspace path to package info
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* const workspaces = await buildWorkspacePackages(lockfile, '/path/to/repo');
|
|
317
|
+
* // => { 'packages/foo': { name: '@scope/foo', version: '1.0.0', dependencies: {...} } }
|
|
318
|
+
*/
|
|
319
|
+
export async function buildWorkspacePackages(input, repoDir) {
|
|
320
|
+
const paths = extractWorkspacePaths(input);
|
|
321
|
+
/** @type {Record<string, WorkspacePackage>} */
|
|
322
|
+
const workspacePackages = {};
|
|
323
|
+
|
|
324
|
+
for (const wsPath of paths) {
|
|
325
|
+
const pkgJsonPath = join(repoDir, wsPath, 'package.json');
|
|
326
|
+
try {
|
|
327
|
+
const pkg = JSON.parse(await readFile(pkgJsonPath, 'utf8'));
|
|
328
|
+
workspacePackages[wsPath] = {
|
|
329
|
+
name: pkg.name,
|
|
330
|
+
version: pkg.version || '0.0.0',
|
|
331
|
+
dependencies: pkg.dependencies,
|
|
332
|
+
devDependencies: pkg.devDependencies,
|
|
333
|
+
optionalDependencies: pkg.optionalDependencies,
|
|
334
|
+
peerDependencies: pkg.peerDependencies
|
|
335
|
+
};
|
|
336
|
+
} catch {
|
|
337
|
+
// Skip workspaces with missing or invalid package.json
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return workspacePackages;
|
|
342
|
+
}
|