flatlock 1.2.0 → 1.4.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.
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * flatlock - Get dependencies from a lockfile
4
+ *
5
+ * For monorepo workspaces, outputs the production dependencies of a workspace.
6
+ * For standalone packages, outputs all production dependencies.
7
+ *
8
+ * Usage:
9
+ * flatlock <lockfile> # all deps (names only)
10
+ * flatlock <lockfile> --specs # name@version
11
+ * flatlock <lockfile> --json # JSON array
12
+ * flatlock <lockfile> --specs --json # JSON with versions
13
+ * flatlock <lockfile> --specs --ndjson # streaming NDJSON
14
+ * flatlock <lockfile> --full --ndjson # full metadata streaming
15
+ */
16
+
17
+ import { parseArgs } from 'node:util';
18
+ import { readFileSync } from 'node:fs';
19
+ import { dirname, join } from 'node:path';
20
+ import { FlatlockSet } from '../src/set.js';
21
+
22
+ const { values, positionals } = parseArgs({
23
+ options: {
24
+ workspace: { type: 'string', short: 'w' },
25
+ dev: { type: 'boolean', default: false },
26
+ peer: { type: 'boolean', default: true },
27
+ specs: { type: 'boolean', short: 's', default: false },
28
+ json: { type: 'boolean', default: false },
29
+ ndjson: { type: 'boolean', default: false },
30
+ full: { type: 'boolean', default: false },
31
+ help: { type: 'boolean', short: 'h' }
32
+ },
33
+ allowPositionals: true
34
+ });
35
+
36
+ if (values.help || positionals.length === 0) {
37
+ console.log(`flatlock - Get dependencies from a lockfile
38
+
39
+ Usage:
40
+ flatlock <lockfile>
41
+ flatlock <lockfile> --workspace <path>
42
+
43
+ Options:
44
+ -w, --workspace <path> Workspace path within monorepo
45
+ -s, --specs Include version (name@version or {name,version})
46
+ --json Output as JSON array
47
+ --ndjson Output as newline-delimited JSON (streaming)
48
+ --full Include all metadata (integrity, resolved)
49
+ --dev Include dev dependencies (default: false)
50
+ --peer Include peer dependencies (default: true)
51
+ -h, --help Show this help
52
+
53
+ Output formats:
54
+ (default) package names, one per line
55
+ --specs package@version, one per line
56
+ --json ["package", ...]
57
+ --specs --json [{"name":"...","version":"..."}, ...]
58
+ --full --json [{"name":"...","version":"...","integrity":"...","resolved":"..."}, ...]
59
+ --ndjson "package" per line
60
+ --specs --ndjson {"name":"...","version":"..."} per line
61
+ --full --ndjson {"name":"...","version":"...","integrity":"...","resolved":"..."} per line
62
+
63
+ Examples:
64
+ flatlock package-lock.json
65
+ flatlock package-lock.json --specs
66
+ flatlock package-lock.json --specs --json
67
+ flatlock package-lock.json --full --ndjson | jq -c 'select(.name | startswith("@babel"))'
68
+ flatlock pnpm-lock.yaml -w packages/core -s --ndjson`);
69
+ process.exit(values.help ? 0 : 1);
70
+ }
71
+
72
+ if (values.json && values.ndjson) {
73
+ console.error('Error: --json and --ndjson are mutually exclusive');
74
+ process.exit(1);
75
+ }
76
+
77
+ // --full implies --specs
78
+ if (values.full) {
79
+ values.specs = true;
80
+ }
81
+
82
+ const lockfilePath = positionals[0];
83
+
84
+ /**
85
+ * Format a single dependency based on output options
86
+ * @param {{ name: string, version: string, integrity?: string, resolved?: string }} dep
87
+ * @param {{ specs: boolean, full: boolean }} options
88
+ * @returns {string | object}
89
+ */
90
+ function formatDep(dep, { specs, full }) {
91
+ if (full) {
92
+ const obj = { name: dep.name, version: dep.version };
93
+ if (dep.integrity) obj.integrity = dep.integrity;
94
+ if (dep.resolved) obj.resolved = dep.resolved;
95
+ return obj;
96
+ }
97
+ if (specs) {
98
+ return { name: dep.name, version: dep.version };
99
+ }
100
+ return dep.name;
101
+ }
102
+
103
+ /**
104
+ * Output dependencies in the requested format
105
+ * @param {Iterable<{ name: string, version: string, integrity?: string, resolved?: string }>} deps
106
+ * @param {{ specs: boolean, json: boolean, ndjson: boolean, full: boolean }} options
107
+ */
108
+ function outputDeps(deps, { specs, json, ndjson, full }) {
109
+ const sorted = [...deps].sort((a, b) => a.name.localeCompare(b.name));
110
+
111
+ if (json) {
112
+ const data = sorted.map(d => formatDep(d, { specs, full }));
113
+ console.log(JSON.stringify(data, null, 2));
114
+ return;
115
+ }
116
+
117
+ if (ndjson) {
118
+ for (const d of sorted) {
119
+ console.log(JSON.stringify(formatDep(d, { specs, full })));
120
+ }
121
+ return;
122
+ }
123
+
124
+ // Plain text
125
+ for (const d of sorted) {
126
+ console.log(specs ? `${d.name}@${d.version}` : d.name);
127
+ }
128
+ }
129
+
130
+ try {
131
+ const lockfile = await FlatlockSet.fromPath(lockfilePath);
132
+ let deps;
133
+
134
+ if (values.workspace) {
135
+ const repoDir = dirname(lockfilePath);
136
+ const workspacePkgPath = join(repoDir, values.workspace, 'package.json');
137
+ const workspacePkg = JSON.parse(readFileSync(workspacePkgPath, 'utf8'));
138
+
139
+ deps = await lockfile.dependenciesOf(workspacePkg, {
140
+ workspacePath: values.workspace,
141
+ repoDir,
142
+ dev: values.dev,
143
+ peer: values.peer
144
+ });
145
+ } else {
146
+ deps = lockfile;
147
+ }
148
+
149
+ outputDeps(deps, {
150
+ specs: values.specs,
151
+ json: values.json,
152
+ ndjson: values.ndjson,
153
+ full: values.full
154
+ });
155
+ } catch (err) {
156
+ console.error(`Error: ${err.message}`);
157
+ process.exit(1);
158
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../src/compare.js"],"names":[],"mappings":"AAyhBA;;;;;GAKG;AACH,kCAJW,MAAM,YACN,cAAc,GACZ,OAAO,CAAC,gBAAgB,CAAC,CA8CrC;AAED;;;;;GAKG;AACH,sCAJW,MAAM,EAAE,YACR,cAAc,GACZ,cAAc,CAAC,gBAAgB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAMnE;AAED;;;GAGG;AACH,uCAFa,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAgB1G;;;;;aAnhBa,MAAM;;;;gBACN,MAAM,EAAE;;;;;;UAKR,MAAM;;;;aACN,MAAM;;;;kBACN,OAAO,GAAG,IAAI;;;;mBACd,MAAM;;;;sBACN,MAAM;;;;qBACN,MAAM;;;;qBACN,MAAM,EAAE;;;;uBACR,MAAM,EAAE;;;;;;cAKR,GAAG,CAAC,MAAM,CAAC;;;;oBACX,MAAM;;;;YACN,MAAM"}
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../src/compare.js"],"names":[],"mappings":"AAyhBA;;;;;GAKG;AACH,kCAJW,MAAM,YACN,cAAc,GACZ,OAAO,CAAC,gBAAgB,CAAC,CA4CrC;AAED;;;;;GAKG;AACH,sCAJW,MAAM,EAAE,YACR,cAAc,GACZ,cAAc,CAAC,gBAAgB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAMnE;AAED;;;GAGG;AACH,uCAFa,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAgB1G;;;;;aAjhBa,MAAM;;;;gBACN,MAAM,EAAE;;;;;;UAKR,MAAM;;;;aACN,MAAM;;;;kBACN,OAAO,GAAG,IAAI;;;;mBACd,MAAM;;;;sBACN,MAAM;;;;qBACN,MAAM;;;;qBACN,MAAM,EAAE;;;;uBACR,MAAM,EAAE;;;;;;cAKR,GAAG,CAAC,MAAM,CAAC;;;;oBACX,MAAM;;;;YACN,MAAM"}
@@ -1,5 +1,5 @@
1
- export { fromPackageLock, parseLockfileKey as parseNpmKey } from "./npm.js";
2
- export { fromPnpmLock, parseLockfileKey as parsePnpmKey } from "./pnpm.js";
3
- export { fromYarnBerryLock, parseLockfileKey as parseYarnBerryKey, parseResolution as parseYarnBerryResolution } from "./yarn-berry.js";
1
+ export { buildWorkspacePackages as buildNpmWorkspacePackages, extractWorkspacePaths as extractNpmWorkspacePaths, fromPackageLock, parseLockfileKey as parseNpmKey } from "./npm.js";
2
+ export { buildWorkspacePackages as buildPnpmWorkspacePackages, extractWorkspacePaths as extractPnpmWorkspacePaths, fromPnpmLock, parseLockfileKey as parsePnpmKey } from "./pnpm.js";
3
+ export { buildWorkspacePackages as buildYarnBerryWorkspacePackages, extractWorkspacePaths as extractYarnBerryWorkspacePaths, fromYarnBerryLock, parseLockfileKey as parseYarnBerryKey, parseResolution as parseYarnBerryResolution } from "./yarn-berry.js";
4
4
  export { fromYarnClassicLock, parseLockfileKey as parseYarnClassicKey, parseYarnClassic } from "./yarn-classic.js";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,13 @@
1
1
  /** @typedef {import('./types.js').Dependency} Dependency */
2
+ /**
3
+ * @typedef {Object} WorkspacePackage
4
+ * @property {string} name
5
+ * @property {string} version
6
+ * @property {Record<string, string>} [dependencies]
7
+ * @property {Record<string, string>} [devDependencies]
8
+ * @property {Record<string, string>} [optionalDependencies]
9
+ * @property {Record<string, string>} [peerDependencies]
10
+ */
2
11
  /**
3
12
  * LIMITATION: Workspace symlinks are not yielded
4
13
  *
@@ -105,5 +114,41 @@ export function parseLockfileKey(path: string): string;
105
114
  * @returns {Generator<Dependency>}
106
115
  */
107
116
  export function fromPackageLock(input: string | object, _options?: Object): Generator<Dependency>;
117
+ /**
118
+ * Extract workspace paths from npm lockfile.
119
+ *
120
+ * npm workspace packages are entries in `packages` that:
121
+ * - Are not the root ('')
122
+ * - Don't contain 'node_modules/' (those are installed deps)
123
+ * - Have a version field
124
+ *
125
+ * @param {string | object} input - Lockfile content string or pre-parsed object
126
+ * @returns {string[]} Array of workspace paths (e.g., ['workspaces/arborist', 'workspaces/libnpmfund'])
127
+ *
128
+ * @example
129
+ * extractWorkspacePaths(lockfile)
130
+ * // => ['workspaces/arborist', 'workspaces/libnpmfund', ...]
131
+ */
132
+ export function extractWorkspacePaths(input: string | object): string[];
133
+ /**
134
+ * Build workspace packages map by reading package.json files.
135
+ *
136
+ * @param {string | object} input - Lockfile content string or pre-parsed object
137
+ * @param {string} repoDir - Path to repository root
138
+ * @returns {Promise<Record<string, WorkspacePackage>>} Map of workspace path to package info
139
+ *
140
+ * @example
141
+ * const workspaces = await buildWorkspacePackages(lockfile, '/path/to/repo');
142
+ * // => { 'workspaces/arborist': { name: '@npmcli/arborist', version: '1.0.0', dependencies: {...} } }
143
+ */
144
+ export function buildWorkspacePackages(input: string | object, repoDir: string): Promise<Record<string, WorkspacePackage>>;
108
145
  export type Dependency = import("./types.js").Dependency;
146
+ export type WorkspacePackage = {
147
+ name: string;
148
+ version: string;
149
+ dependencies?: Record<string, string>;
150
+ devDependencies?: Record<string, string>;
151
+ optionalDependencies?: Record<string, string>;
152
+ peerDependencies?: Record<string, string>;
153
+ };
109
154
  //# sourceMappingURL=npm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../src/parsers/npm.js"],"names":[],"mappings":"AAAA,4DAA4D;AAE5D;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AACH,uCA/DW,MAAM,GACJ,MAAM,CAoElB;AAED;;;;;GAKG;AACH,uCAJW,MAAM,GAAG,MAAM,aACf,MAAM,GACJ,SAAS,CAAC,UAAU,CAAC,CA6BjC;yBA9Ia,OAAO,YAAY,EAAE,UAAU"}
1
+ {"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../src/parsers/npm.js"],"names":[],"mappings":"AAGA,4DAA4D;AAE5D;;;;;;;;GAQG;AAEH;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AACH,uCA/DW,MAAM,GACJ,MAAM,CAoElB;AAED;;;;;GAKG;AACH,uCAJW,MAAM,GAAG,MAAM,aACf,MAAM,GACJ,SAAS,CAAC,UAAU,CAAC,CA6BjC;AAED;;;;;;;;;;;;;;GAcG;AACH,6CAPW,MAAM,GAAG,MAAM,GACb,MAAM,EAAE,CAsBpB;AAED;;;;;;;;;;GAUG;AACH,8CARW,MAAM,GAAG,MAAM,WACf,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CA6BrD;yBA7Na,OAAO,YAAY,EAAE,UAAU;;UAI/B,MAAM;aACN,MAAM;mBACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;sBACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;2BACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;uBACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC"}
@@ -115,6 +115,40 @@ export function parseLockfileKey(key: string): string | null;
115
115
  * const deps = [...fromPnpmLock(lockfile)];
116
116
  */
117
117
  export function fromPnpmLock(input: string | object, _options?: Object): Generator<Dependency>;
118
+ /**
119
+ * Extract workspace paths from pnpm lockfile.
120
+ *
121
+ * pnpm stores workspace packages in the `importers` section.
122
+ * Each key is a workspace path relative to the repo root.
123
+ *
124
+ * @param {string | object} input - Lockfile content string or pre-parsed object
125
+ * @returns {string[]} Array of workspace paths (e.g., ['packages/foo', 'packages/bar'])
126
+ *
127
+ * @example
128
+ * extractWorkspacePaths(lockfile)
129
+ * // => ['packages/vue', 'packages/compiler-core', ...]
130
+ */
131
+ export function extractWorkspacePaths(input: string | object): string[];
132
+ /**
133
+ * Build workspace packages map by reading package.json files.
134
+ *
135
+ * @param {string | object} input - Lockfile content string or pre-parsed object
136
+ * @param {string} repoDir - Path to repository root
137
+ * @returns {Promise<Record<string, WorkspacePackage>>} Map of workspace path to package info
138
+ *
139
+ * @example
140
+ * const workspaces = await buildWorkspacePackages(lockfile, '/path/to/repo');
141
+ * // => { 'packages/foo': { name: '@scope/foo', version: '1.0.0', dependencies: {...} } }
142
+ */
143
+ export function buildWorkspacePackages(input: string | object, repoDir: string): Promise<Record<string, WorkspacePackage>>;
118
144
  export { detectVersion } from "./detect.js";
119
145
  export type Dependency = import("../types.js").Dependency;
146
+ export type WorkspacePackage = {
147
+ name: string;
148
+ version: string;
149
+ dependencies?: Record<string, string>;
150
+ devDependencies?: Record<string, string>;
151
+ optionalDependencies?: Record<string, string>;
152
+ peerDependencies?: Record<string, string>;
153
+ };
120
154
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/parsers/pnpm/index.js"],"names":[],"mappings":"AA2BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AACH,gCAzEW,MAAM,GACJ;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA+H3D;AAED;;;;;;;;;;GAUG;AACH,sCAPW,MAAM,GACJ,MAAM,GAAG,IAAI,CAQzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,oCAbW,MAAM,GAAG,MAAM,aACf,MAAM,GACJ,SAAS,CAAC,UAAU,CAAC,CAoGjC;;yBA5Qa,OAAO,aAAa,EAAE,UAAU"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/parsers/pnpm/index.js"],"names":[],"mappings":"AAuCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AACH,gCAzEW,MAAM,GACJ;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA+H3D;AAED;;;;;;;;;;GAUG;AACH,sCAPW,MAAM,GACJ,MAAM,GAAG,IAAI,CAQzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,oCAbW,MAAM,GAAG,MAAM,aACf,MAAM,GACJ,SAAS,CAAC,UAAU,CAAC,CAoGjC;AAED;;;;;;;;;;;;GAYG;AACH,6CAPW,MAAM,GAAG,MAAM,GACb,MAAM,EAAE,CAapB;AAED;;;;;;;;;;GAUG;AACH,8CARW,MAAM,GAAG,MAAM,WACf,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CA6BrD;;yBAhVa,OAAO,aAAa,EAAE,UAAU;;UAIhC,MAAM;aACN,MAAM;mBACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;sBACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;2BACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;uBACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC"}
@@ -1,4 +1,13 @@
1
1
  /** @typedef {import('./types.js').Dependency} Dependency */
2
+ /**
3
+ * @typedef {Object} WorkspacePackage
4
+ * @property {string} name
5
+ * @property {string} version
6
+ * @property {Record<string, string>} [dependencies]
7
+ * @property {Record<string, string>} [devDependencies]
8
+ * @property {Record<string, string>} [optionalDependencies]
9
+ * @property {Record<string, string>} [peerDependencies]
10
+ */
2
11
  /**
3
12
  * Extract package name from yarn berry resolution field.
4
13
  *
@@ -150,5 +159,39 @@ export function parseLockfileKey(key: string): string;
150
159
  * @returns {Generator<Dependency>}
151
160
  */
152
161
  export function fromYarnBerryLock(input: string | object, _options?: Object): Generator<Dependency>;
162
+ /**
163
+ * Extract workspace paths from yarn berry lockfile.
164
+ *
165
+ * Yarn berry workspace entries use `@workspace:` protocol in keys.
166
+ * Keys can have multiple comma-separated descriptors.
167
+ *
168
+ * @param {string | object} input - Lockfile content string or pre-parsed object
169
+ * @returns {string[]} Array of workspace paths (e.g., ['packages/foo', 'packages/bar'])
170
+ *
171
+ * @example
172
+ * extractWorkspacePaths(lockfile)
173
+ * // => ['packages/babel-core', 'packages/babel-parser', ...]
174
+ */
175
+ export function extractWorkspacePaths(input: string | object): string[];
176
+ /**
177
+ * Build workspace packages map by reading package.json files.
178
+ *
179
+ * @param {string | object} input - Lockfile content string or pre-parsed object
180
+ * @param {string} repoDir - Path to repository root
181
+ * @returns {Promise<Record<string, WorkspacePackage>>} Map of workspace path to package info
182
+ *
183
+ * @example
184
+ * const workspaces = await buildWorkspacePackages(lockfile, '/path/to/repo');
185
+ * // => { 'packages/foo': { name: '@scope/foo', version: '1.0.0', dependencies: {...} } }
186
+ */
187
+ export function buildWorkspacePackages(input: string | object, repoDir: string): Promise<Record<string, WorkspacePackage>>;
153
188
  export type Dependency = import("./types.js").Dependency;
189
+ export type WorkspacePackage = {
190
+ name: string;
191
+ version: string;
192
+ dependencies?: Record<string, string>;
193
+ devDependencies?: Record<string, string>;
194
+ optionalDependencies?: Record<string, string>;
195
+ peerDependencies?: Record<string, string>;
196
+ };
154
197
  //# sourceMappingURL=yarn-berry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"yarn-berry.d.ts","sourceRoot":"","sources":["../../src/parsers/yarn-berry.js"],"names":[],"mappings":"AAEA,4DAA4D;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AACH,4CAjEW,MAAM,GACJ,MAAM,GAAG,IAAI,CA4FzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AACH,sCAhEW,MAAM,GACJ,MAAM,CAiGlB;AAED;;;;;GAKG;AACH,yCAJW,MAAM,GAAG,MAAM,aACf,MAAM,GACJ,SAAS,CAAC,UAAU,CAAC,CAqCjC;yBA3Pa,OAAO,YAAY,EAAE,UAAU"}
1
+ {"version":3,"file":"yarn-berry.d.ts","sourceRoot":"","sources":["../../src/parsers/yarn-berry.js"],"names":[],"mappings":"AAIA,4DAA4D;AAE5D;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AACH,4CAjEW,MAAM,GACJ,MAAM,GAAG,IAAI,CA4FzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AACH,sCAhEW,MAAM,GACJ,MAAM,CAiGlB;AAED;;;;;GAKG;AACH,yCAJW,MAAM,GAAG,MAAM,aACf,MAAM,GACJ,SAAS,CAAC,UAAU,CAAC,CAqCjC;AAED;;;;;;;;;;;;GAYG;AACH,6CAPW,MAAM,GAAG,MAAM,GACb,MAAM,EAAE,CA+BpB;AAED;;;;;;;;;;GAUG;AACH,8CARW,MAAM,GAAG,MAAM,WACf,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CA6BrD;yBAjVa,OAAO,YAAY,EAAE,UAAU;;UAI/B,MAAM;aACN,MAAM;mBACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;sBACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;2BACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;uBACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC"}
package/dist/set.d.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  *
17
17
  * // Get dependencies for a specific workspace
18
18
  * const pkg = JSON.parse(await readFile('./packages/foo/package.json'));
19
- * const subset = set.dependenciesOf(pkg, { workspacePath: 'packages/foo' });
19
+ * const subset = await set.dependenciesOf(pkg, { workspacePath: 'packages/foo', repoDir: '.' });
20
20
  *
21
21
  * // Set operations
22
22
  * const other = await FlatlockSet.fromPath('./other-lock.json');
@@ -42,27 +42,35 @@ export class FlatlockSet {
42
42
  * @param {string} content
43
43
  * @param {LockfileType} type
44
44
  * @param {FromStringOptions} options
45
- * @returns {{ deps: Map<string, Dependency>, packages: LockfilePackages, importers: LockfileImporters | null }}
45
+ * @returns {{ deps: Map<string, Dependency>, packages: LockfilePackages, importers: LockfileImporters | null, snapshots: Record<string, any> | null }}
46
46
  */
47
47
  static "__#private@#parseAll"(content: string, type: LockfileType, options: FromStringOptions): {
48
48
  deps: Map<string, Dependency>;
49
49
  packages: LockfilePackages;
50
50
  importers: LockfileImporters | null;
51
+ snapshots: Record<string, any> | null;
51
52
  };
52
53
  /**
53
54
  * @param {symbol} internal - Must be INTERNAL symbol
54
55
  * @param {Map<string, Dependency>} deps
55
56
  * @param {LockfilePackages | null} packages
56
57
  * @param {LockfileImporters | null} importers
58
+ * @param {Record<string, any> | null} snapshots
57
59
  * @param {LockfileType | null} type
58
60
  */
59
- constructor(internal: symbol, deps: Map<string, Dependency>, packages: LockfilePackages | null, importers: LockfileImporters | null, type: LockfileType | null);
61
+ constructor(internal: symbol, deps: Map<string, Dependency>, packages: LockfilePackages | null, importers: LockfileImporters | null, snapshots: Record<string, any> | null, type: LockfileType | null);
60
62
  /** @returns {number} */
61
63
  get size(): number;
62
64
  /** @returns {LockfileType | null} */
63
65
  get type(): LockfileType | null;
64
66
  /** @returns {boolean} */
65
67
  get canTraverse(): boolean;
68
+ /**
69
+ * Get workspace paths from lockfile.
70
+ * Supports npm, pnpm, and yarn berry lockfiles.
71
+ * @returns {string[]} Array of workspace paths (e.g., ['packages/foo', 'packages/bar'])
72
+ */
73
+ getWorkspacePaths(): string[];
66
74
  /**
67
75
  * Check if a dependency exists
68
76
  * @param {string} nameAtVersion - e.g., "lodash@4.17.21"
@@ -126,7 +134,7 @@ export class FlatlockSet {
126
134
  /**
127
135
  * Get transitive dependencies of a package.json
128
136
  *
129
- * For monorepos, provide workspacePath to get correct resolution.
137
+ * For monorepos, provide workspacePath and repoDir to get correct resolution.
130
138
  * Without workspacePath, assumes root package (hoisted deps only).
131
139
  *
132
140
  * NOTE: This method is only available on sets created directly from
@@ -135,10 +143,17 @@ export class FlatlockSet {
135
143
  *
136
144
  * @param {PackageJson} packageJson - Parsed package.json
137
145
  * @param {DependenciesOfOptions} [options]
138
- * @returns {FlatlockSet}
146
+ * @returns {Promise<FlatlockSet>}
139
147
  * @throws {Error} If called on a set that cannot traverse
148
+ *
149
+ * @example
150
+ * // Simple usage with repoDir (recommended)
151
+ * const deps = await lockfile.dependenciesOf(pkg, {
152
+ * workspacePath: 'packages/foo',
153
+ * repoDir: '/path/to/repo'
154
+ * });
140
155
  */
141
- dependenciesOf(packageJson: PackageJson, options?: DependenciesOfOptions): FlatlockSet;
156
+ dependenciesOf(packageJson: PackageJson, options?: DependenciesOfOptions): Promise<FlatlockSet>;
142
157
  /**
143
158
  * Convert to array
144
159
  * @returns {Dependency[]}
@@ -150,11 +165,41 @@ export class FlatlockSet {
150
165
  }
151
166
  export type Dependency = import("./parsers/npm.js").Dependency;
152
167
  export type LockfileType = import("./detect.js").LockfileType;
168
+ export type WorkspacePackage = {
169
+ /**
170
+ * - Package name (e.g., '@vue/shared')
171
+ */
172
+ name: string;
173
+ /**
174
+ * - Package version (e.g., '3.5.26')
175
+ */
176
+ version: string;
177
+ /**
178
+ * - Production dependencies (for yarn berry workspace traversal)
179
+ */
180
+ dependencies?: Record<string, string>;
181
+ /**
182
+ * - Dev dependencies
183
+ */
184
+ devDependencies?: Record<string, string>;
185
+ /**
186
+ * - Optional dependencies
187
+ */
188
+ optionalDependencies?: Record<string, string>;
189
+ /**
190
+ * - Peer dependencies
191
+ */
192
+ peerDependencies?: Record<string, string>;
193
+ };
153
194
  export type DependenciesOfOptions = {
154
195
  /**
155
196
  * - Path to workspace (e.g., 'packages/foo')
156
197
  */
157
198
  workspacePath?: string;
199
+ /**
200
+ * - Path to repository root for reading workspace package.json files
201
+ */
202
+ repoDir?: string;
158
203
  /**
159
204
  * - Include devDependencies
160
205
  */
@@ -167,6 +212,10 @@ export type DependenciesOfOptions = {
167
212
  * - Include peerDependencies (default false: peers are provided by consumer)
168
213
  */
169
214
  peer?: boolean;
215
+ /**
216
+ * - Map of workspace path to package info for resolving workspace links (auto-built if repoDir provided)
217
+ */
218
+ workspacePackages?: Record<string, WorkspacePackage>;
170
219
  };
171
220
  export type FromStringOptions = {
172
221
  /**
package/dist/set.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../src/set.js"],"names":[],"mappings":"AAoDA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;IAoCE;;;;;OAKG;IACH,sBAJW,MAAM,YACN,iBAAiB,GACf,OAAO,CAAC,WAAW,CAAC,CAKhC;IAED;;;;;OAKG;IACH,2BAJW,MAAM,YACN,iBAAiB,GACf,WAAW,CAgBvB;IAED;;;;;;OAMG;IACH,uCALW,MAAM,QACN,YAAY,WACZ,iBAAiB,GACf;QAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAAC,QAAQ,EAAE,gBAAgB,CAAC;QAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAA;KAAE,CAmD9G;IA7GD;;;;;;OAMG;IACH,sBANW,MAAM,QACN,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,YACvB,gBAAgB,GAAG,IAAI,aACvB,iBAAiB,GAAG,IAAI,QACxB,YAAY,GAAG,IAAI,EAa7B;IA6FD,wBAAwB;IACxB,YADc,MAAM,CAGnB;IAED,qCAAqC;IACrC,YADc,YAAY,GAAG,IAAI,CAGhC;IAED,yBAAyB;IACzB,mBADc,OAAO,CAGpB;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACJ,OAAO,CAInB;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACJ,UAAU,GAAG,SAAS,CAIlC;IAOD,8CAA8C;IAC9C,UADc,gBAAgB,CAAC,UAAU,CAAC,CAGzC;IAED,0CAA0C;IAC1C,QADc,gBAAgB,CAAC,MAAM,CAAC,CAGrC;IAED,wDAAwD;IACxD,WADc,gBAAgB,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAGnD;IAED;;;;OAIG;IACH,kBAHW,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,KAAK,IAAI,YACxD,GAAG,QAMb;IAED;;;;OAIG;IACH,aAHW,WAAW,GACT,WAAW,CAUvB;IAED;;;;OAIG;IACH,oBAHW,WAAW,GACT,WAAW,CAUvB;IAED;;;;OAIG;IACH,kBAHW,WAAW,GACT,WAAW,CAUvB;IAED;;;;OAIG;IACH,kBAHW,WAAW,GACT,OAAO,CAOnB;IAED;;;;OAIG;IACH,oBAHW,WAAW,GACT,OAAO,CAInB;IAED;;;;OAIG;IACH,sBAHW,WAAW,GACT,OAAO,CAOnB;IAED;;;;;;;;;;;;;;OAcG;IACH,4BALW,WAAW,YACX,qBAAqB,GACnB,WAAW,CA4BvB;IAiOD;;;OAGG;IACH,WAFa,UAAU,EAAE,CAIxB;IA5XD,8CAA8C;IAC9C,qBADc,gBAAgB,CAAC,UAAU,CAAC,CAGzC;;CA0XF;yBA1lBY,OAAO,kBAAkB,EAAE,UAAU;2BACrC,OAAO,aAAa,EAAE,YAAY;;;;;oBAKjC,MAAM;;;;UACN,OAAO;;;;eACP,OAAO;;;;WACP,OAAO;;;;;;WAKP,MAAM;;;;WACN,YAAY;;;mBAKZ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;sBACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;2BACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;uBACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;;+BAIvB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;gCAInB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC"}
1
+ {"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../src/set.js"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flatlock",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "The Matlock of lockfile parsers - extracts packages without building dependency graphs",
5
5
  "keywords": [
6
6
  "lockfile",
@@ -35,12 +35,16 @@
35
35
  },
36
36
  "main": "src/index.js",
37
37
  "bin": {
38
- "flatlock-cmp": "./bin/flatlock-cmp.js"
38
+ "flatlock": "bin/flatlock.js",
39
+ "flatlock-cmp": "bin/flatlock-cmp.js",
40
+ "flatcover": "bin/flatcover.js"
39
41
  },
40
42
  "files": [
41
43
  "src",
42
44
  "dist",
43
- "bin"
45
+ "bin/flatlock.js",
46
+ "bin/flatlock-cmp.js",
47
+ "bin/flatcover.js"
44
48
  ],
45
49
  "scripts": {
46
50
  "test": "node --test ./test/*.test.js ./test/**/*.test.js",
@@ -53,29 +57,41 @@
53
57
  "format:check": "biome format src test",
54
58
  "check": "biome check src test && pnpm run build:types",
55
59
  "check:fix": "biome check --write src test",
60
+ "build:ncc": "./bin/ncc.sh",
56
61
  "prepublishOnly": "pnpm run build:types"
57
62
  },
58
63
  "dependencies": {
59
64
  "@yarnpkg/lockfile": "^1.1.0",
60
65
  "@yarnpkg/parsers": "^3.0.3",
61
- "js-yaml": "^4.1.1"
66
+ "js-yaml": "^4.1.1",
67
+ "undici": "^7.0.0"
62
68
  },
63
69
  "devDependencies": {
64
70
  "@biomejs/biome": "^2.3.8",
71
+ "@vercel/ncc": "^0.38.4",
65
72
  "@types/js-yaml": "^4.0.9",
66
73
  "@types/node": "^22.10.2",
67
74
  "c8": "^10.1.3",
75
+ "chalk": "^5.6.2",
76
+ "fast-glob": "^3.3.3",
77
+ "jackspeak": "^4.1.1",
68
78
  "markdownlint-cli2": "^0.17.2",
69
79
  "snyk-nodejs-lockfile-parser": "^1.55.0",
80
+ "tinyexec": "^1.0.2",
70
81
  "typescript": "^5.7.2"
71
82
  },
72
83
  "optionalDependencies": {
73
- "@cyclonedx/cyclonedx-npm": "^4.1.2",
84
+ "@cyclonedx/cdxgen": "^11.3.3",
74
85
  "@npmcli/arborist": "^9.1.9",
75
86
  "@pnpm/lockfile.fs": "^1001.0.0",
76
87
  "@yarnpkg/core": "^4.5.0"
77
88
  },
78
89
  "packageManager": "pnpm@10.25.0",
90
+ "pnpm": {
91
+ "overrides": {
92
+ "tar": ">=7.5.3"
93
+ }
94
+ },
79
95
  "engines": {
80
96
  "node": ">=22"
81
97
  },
package/src/compare.js CHANGED
@@ -6,7 +6,7 @@ import { tmpdir } from 'node:os';
6
6
  import { dirname, join } from 'node:path';
7
7
  import { parseSyml } from '@yarnpkg/parsers';
8
8
  import yaml from 'js-yaml';
9
- import { detectType, fromPath, Type } from './index.js';
9
+ import { collect, detectType, Type } from './index.js';
10
10
  import { parseYarnBerryKey, parseYarnClassic, parseYarnClassicKey } from './parsers/index.js';
11
11
  import { parseSpec as parsePnpmSpec } from './parsers/pnpm.js';
12
12
 
@@ -545,12 +545,10 @@ export async function compare(filepath, options = {}) {
545
545
  const content = await readFile(filepath, 'utf8');
546
546
  const type = detectType({ path: filepath, content });
547
547
 
548
- const flatlockSet = new Set();
549
- for await (const dep of fromPath(filepath)) {
550
- if (dep.name && dep.version) {
551
- flatlockSet.add(`${dep.name}@${dep.version}`);
552
- }
553
- }
548
+ console.time(`⏱ collect [...]${filepath.slice(-40)}`);
549
+ const deps = await collect(filepath);
550
+ console.timeEnd(`⏱ collect [...]${filepath.slice(-40)}`);
551
+ const flatlockSet = new Set(deps.map(({ name, version }) => `${name}@${version}`));
554
552
 
555
553
  let comparisonResult;
556
554
  switch (type) {
@@ -2,9 +2,21 @@
2
2
  * Re-export all lockfile parsers
3
3
  */
4
4
 
5
- export { fromPackageLock, parseLockfileKey as parseNpmKey } from './npm.js';
6
- export { fromPnpmLock, parseLockfileKey as parsePnpmKey } from './pnpm.js';
7
5
  export {
6
+ buildWorkspacePackages as buildNpmWorkspacePackages,
7
+ extractWorkspacePaths as extractNpmWorkspacePaths,
8
+ fromPackageLock,
9
+ parseLockfileKey as parseNpmKey
10
+ } from './npm.js';
11
+ export {
12
+ buildWorkspacePackages as buildPnpmWorkspacePackages,
13
+ extractWorkspacePaths as extractPnpmWorkspacePaths,
14
+ fromPnpmLock,
15
+ parseLockfileKey as parsePnpmKey
16
+ } from './pnpm.js';
17
+ export {
18
+ buildWorkspacePackages as buildYarnBerryWorkspacePackages,
19
+ extractWorkspacePaths as extractYarnBerryWorkspacePaths,
8
20
  fromYarnBerryLock,
9
21
  parseLockfileKey as parseYarnBerryKey,
10
22
  parseResolution as parseYarnBerryResolution