@vltpkg/graph 0.0.0-9 → 1.0.0-rc.10
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 +136 -1
- package/dist/esm/actual/load.d.ts +49 -3
- package/dist/esm/actual/load.d.ts.map +1 -1
- package/dist/esm/actual/load.js +146 -74
- package/dist/esm/actual/load.js.map +1 -1
- package/dist/esm/browser.d.ts +8 -4
- package/dist/esm/browser.d.ts.map +1 -1
- package/dist/esm/browser.js +6 -2
- package/dist/esm/browser.js.map +1 -1
- package/dist/esm/build.d.ts +29 -0
- package/dist/esm/build.d.ts.map +1 -0
- package/dist/esm/build.js +79 -0
- package/dist/esm/build.js.map +1 -0
- package/dist/esm/dependencies.d.ts +10 -3
- package/dist/esm/dependencies.d.ts.map +1 -1
- package/dist/esm/dependencies.js +63 -0
- package/dist/esm/dependencies.js.map +1 -1
- package/dist/esm/diff.d.ts +69 -0
- package/dist/esm/diff.d.ts.map +1 -1
- package/dist/esm/diff.js +25 -0
- package/dist/esm/diff.js.map +1 -1
- package/dist/esm/edge.d.ts +8 -2
- package/dist/esm/edge.d.ts.map +1 -1
- package/dist/esm/edge.js +12 -0
- package/dist/esm/edge.js.map +1 -1
- package/dist/esm/fixup-added-names.d.ts +16 -0
- package/dist/esm/fixup-added-names.d.ts.map +1 -0
- package/dist/esm/fixup-added-names.js +31 -0
- package/dist/esm/fixup-added-names.js.map +1 -0
- package/dist/esm/graph.d.ts +34 -10
- package/dist/esm/graph.d.ts.map +1 -1
- package/dist/esm/graph.js +150 -24
- package/dist/esm/graph.js.map +1 -1
- package/dist/esm/ideal/append-nodes.d.ts +12 -1
- package/dist/esm/ideal/append-nodes.d.ts.map +1 -1
- package/dist/esm/ideal/append-nodes.js +303 -53
- package/dist/esm/ideal/append-nodes.js.map +1 -1
- package/dist/esm/ideal/build-ideal-from-starting-graph.d.ts +4 -4
- package/dist/esm/ideal/build-ideal-from-starting-graph.d.ts.map +1 -1
- package/dist/esm/ideal/build-ideal-from-starting-graph.js +35 -16
- package/dist/esm/ideal/build-ideal-from-starting-graph.js.map +1 -1
- package/dist/esm/ideal/build.d.ts +9 -0
- package/dist/esm/ideal/build.d.ts.map +1 -1
- package/dist/esm/ideal/build.js +31 -1
- package/dist/esm/ideal/build.js.map +1 -1
- package/dist/esm/ideal/get-importer-specs.d.ts +11 -3
- package/dist/esm/ideal/get-importer-specs.d.ts.map +1 -1
- package/dist/esm/ideal/get-importer-specs.js +86 -9
- package/dist/esm/ideal/get-importer-specs.js.map +1 -1
- package/dist/esm/ideal/get-ordered-dependencies.d.ts +10 -0
- package/dist/esm/ideal/get-ordered-dependencies.d.ts.map +1 -0
- package/dist/esm/ideal/get-ordered-dependencies.js +42 -0
- package/dist/esm/ideal/get-ordered-dependencies.js.map +1 -0
- package/dist/esm/ideal/peers.d.ts +71 -0
- package/dist/esm/ideal/peers.d.ts.map +1 -0
- package/dist/esm/ideal/peers.js +318 -0
- package/dist/esm/ideal/peers.js.map +1 -0
- package/dist/esm/ideal/refresh-ideal-graph.d.ts +48 -0
- package/dist/esm/ideal/refresh-ideal-graph.d.ts.map +1 -0
- package/dist/esm/ideal/refresh-ideal-graph.js +79 -0
- package/dist/esm/ideal/refresh-ideal-graph.js.map +1 -0
- package/dist/esm/ideal/types.d.ts +78 -1
- package/dist/esm/ideal/types.d.ts.map +1 -1
- package/dist/esm/ideal/types.js.map +1 -1
- package/dist/esm/index.d.ts +9 -4
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +5 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/install.d.ts +10 -4
- package/dist/esm/install.d.ts.map +1 -1
- package/dist/esm/install.js +191 -20
- package/dist/esm/install.js.map +1 -1
- package/dist/esm/lockfile/load-edges.d.ts +8 -1
- package/dist/esm/lockfile/load-edges.d.ts.map +1 -1
- package/dist/esm/lockfile/load-edges.js +80 -15
- package/dist/esm/lockfile/load-edges.js.map +1 -1
- package/dist/esm/lockfile/load-nodes.d.ts +3 -2
- package/dist/esm/lockfile/load-nodes.d.ts.map +1 -1
- package/dist/esm/lockfile/load-nodes.js +85 -13
- package/dist/esm/lockfile/load-nodes.js.map +1 -1
- package/dist/esm/lockfile/load.d.ts +18 -5
- package/dist/esm/lockfile/load.d.ts.map +1 -1
- package/dist/esm/lockfile/load.js +28 -20
- package/dist/esm/lockfile/load.js.map +1 -1
- package/dist/esm/lockfile/save.d.ts +16 -3
- package/dist/esm/lockfile/save.d.ts.map +1 -1
- package/dist/esm/lockfile/save.js +64 -17
- package/dist/esm/lockfile/save.js.map +1 -1
- package/dist/esm/lockfile/types.d.ts +34 -4
- package/dist/esm/lockfile/types.d.ts.map +1 -1
- package/dist/esm/lockfile/types.js +31 -0
- package/dist/esm/lockfile/types.js.map +1 -1
- package/dist/esm/modifiers.d.ts +189 -0
- package/dist/esm/modifiers.d.ts.map +1 -0
- package/dist/esm/modifiers.js +330 -0
- package/dist/esm/modifiers.js.map +1 -0
- package/dist/esm/node.d.ts +91 -6
- package/dist/esm/node.d.ts.map +1 -1
- package/dist/esm/node.js +119 -5
- package/dist/esm/node.js.map +1 -1
- package/dist/esm/reify/add-edge.d.ts +1 -2
- package/dist/esm/reify/add-edge.d.ts.map +1 -1
- package/dist/esm/reify/add-edge.js +29 -18
- package/dist/esm/reify/add-edge.js.map +1 -1
- package/dist/esm/reify/add-edges.d.ts +1 -2
- package/dist/esm/reify/add-edges.d.ts.map +1 -1
- package/dist/esm/reify/add-edges.js +3 -3
- package/dist/esm/reify/add-edges.js.map +1 -1
- package/dist/esm/reify/add-nodes.d.ts.map +1 -1
- package/dist/esm/reify/add-nodes.js +4 -27
- package/dist/esm/reify/add-nodes.js.map +1 -1
- package/dist/esm/reify/bin-chmod.d.ts +11 -0
- package/dist/esm/reify/bin-chmod.d.ts.map +1 -0
- package/dist/esm/reify/bin-chmod.js +39 -0
- package/dist/esm/reify/bin-chmod.js.map +1 -0
- package/dist/esm/reify/build.d.ts +10 -1
- package/dist/esm/reify/build.d.ts.map +1 -1
- package/dist/esm/reify/build.js +36 -23
- package/dist/esm/reify/build.js.map +1 -1
- package/dist/esm/reify/calculate-save-value.d.ts +3 -0
- package/dist/esm/reify/calculate-save-value.d.ts.map +1 -0
- package/dist/esm/reify/calculate-save-value.js +45 -0
- package/dist/esm/reify/calculate-save-value.js.map +1 -0
- package/dist/esm/reify/check-needed-build.d.ts +25 -0
- package/dist/esm/reify/check-needed-build.d.ts.map +1 -0
- package/dist/esm/reify/check-needed-build.js +50 -0
- package/dist/esm/reify/check-needed-build.js.map +1 -0
- package/dist/esm/reify/delete-edge.d.ts.map +1 -1
- package/dist/esm/reify/delete-edge.js +3 -4
- package/dist/esm/reify/delete-edge.js.map +1 -1
- package/dist/esm/reify/extract-node.d.ts +24 -0
- package/dist/esm/reify/extract-node.d.ts.map +1 -0
- package/dist/esm/reify/extract-node.js +84 -0
- package/dist/esm/reify/extract-node.js.map +1 -0
- package/dist/esm/reify/index.d.ts +18 -1
- package/dist/esm/reify/index.d.ts.map +1 -1
- package/dist/esm/reify/index.js +85 -15
- package/dist/esm/reify/index.js.map +1 -1
- package/dist/esm/reify/internal-hoist.d.ts +9 -0
- package/dist/esm/reify/internal-hoist.d.ts.map +1 -0
- package/dist/esm/reify/internal-hoist.js +134 -0
- package/dist/esm/reify/internal-hoist.js.map +1 -0
- package/dist/esm/reify/update-importers-package-json.d.ts +1 -1
- package/dist/esm/reify/update-importers-package-json.d.ts.map +1 -1
- package/dist/esm/reify/update-importers-package-json.js +33 -24
- package/dist/esm/reify/update-importers-package-json.js.map +1 -1
- package/dist/esm/remove-optional-subgraph.js +1 -1
- package/dist/esm/remove-optional-subgraph.js.map +1 -1
- package/dist/esm/resolve-save-type.d.ts +1 -2
- package/dist/esm/resolve-save-type.d.ts.map +1 -1
- package/dist/esm/resolve-save-type.js.map +1 -1
- package/dist/esm/stringify-node.d.ts +1 -1
- package/dist/esm/stringify-node.d.ts.map +1 -1
- package/dist/esm/stringify-node.js +10 -1
- package/dist/esm/stringify-node.js.map +1 -1
- package/dist/esm/transfer-data/load.d.ts +44 -0
- package/dist/esm/transfer-data/load.d.ts.map +1 -0
- package/dist/esm/transfer-data/load.js +176 -0
- package/dist/esm/transfer-data/load.js.map +1 -0
- package/dist/esm/uninstall.d.ts +5 -4
- package/dist/esm/uninstall.d.ts.map +1 -1
- package/dist/esm/uninstall.js +61 -19
- package/dist/esm/uninstall.js.map +1 -1
- package/dist/esm/update.d.ts +13 -0
- package/dist/esm/update.d.ts.map +1 -0
- package/dist/esm/update.js +73 -0
- package/dist/esm/update.js.map +1 -0
- package/dist/esm/virtual-root.d.ts +16 -0
- package/dist/esm/virtual-root.d.ts.map +1 -0
- package/dist/esm/virtual-root.js +79 -0
- package/dist/esm/virtual-root.js.map +1 -0
- package/dist/esm/visualization/human-readable-output.d.ts +4 -5
- package/dist/esm/visualization/human-readable-output.d.ts.map +1 -1
- package/dist/esm/visualization/human-readable-output.js +47 -19
- package/dist/esm/visualization/human-readable-output.js.map +1 -1
- package/dist/esm/visualization/json-output.d.ts +7 -2
- package/dist/esm/visualization/json-output.d.ts.map +1 -1
- package/dist/esm/visualization/json-output.js +35 -12
- package/dist/esm/visualization/json-output.js.map +1 -1
- package/dist/esm/visualization/mermaid-output.d.ts +7 -1
- package/dist/esm/visualization/mermaid-output.d.ts.map +1 -1
- package/dist/esm/visualization/mermaid-output.js +110 -14
- package/dist/esm/visualization/mermaid-output.js.map +1 -1
- package/dist/esm/visualization/object-like-output.d.ts +1 -1
- package/dist/esm/visualization/object-like-output.d.ts.map +1 -1
- package/dist/esm/visualization/object-like-output.js.map +1 -1
- package/package.json +35 -29
- package/dist/esm/ideal/add-nodes.d.ts +0 -19
- package/dist/esm/ideal/add-nodes.d.ts.map +0 -1
- package/dist/esm/ideal/add-nodes.js +0 -32
- package/dist/esm/ideal/add-nodes.js.map +0 -1
- package/dist/esm/ideal/remove-nodes.d.ts +0 -7
- package/dist/esm/ideal/remove-nodes.d.ts.map +0 -1
- package/dist/esm/ideal/remove-nodes.js +0 -19
- package/dist/esm/ideal/remove-nodes.js.map +0 -1
- package/dist/esm/reify/bin-paths.d.ts +0 -4
- package/dist/esm/reify/bin-paths.d.ts.map +0 -1
- package/dist/esm/reify/bin-paths.js +0 -23
- package/dist/esm/reify/bin-paths.js.map +0 -1
- package/dist/esm/types.d.ts +0 -42
- package/dist/esm/types.d.ts.map +0 -1
- package/dist/esm/types.js +0 -2
- package/dist/esm/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -5,7 +5,44 @@
|
|
|
5
5
|
This is the graph library responsible for representing the packages
|
|
6
6
|
that are involved in a given install.
|
|
7
7
|
|
|
8
|
-
**[
|
|
8
|
+
**[Overview](#overview)** · **[Concepts](#concepts)** ·
|
|
9
|
+
**[Architecture](#architecture)** · **[API](#api)** ·
|
|
10
|
+
**[Usage](#usage)** · **[Related Workspaces](#related-workspaces)** ·
|
|
11
|
+
**[References](#references)**
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
The `@vltpkg/graph` workspace models a project's dependency
|
|
16
|
+
relationships and drives npm-compatible installs by computing how
|
|
17
|
+
`node_modules` should be structured. It exposes a public API through
|
|
18
|
+
`src/index.ts` that re-exports core types and workflows.
|
|
19
|
+
|
|
20
|
+
At a glance:
|
|
21
|
+
|
|
22
|
+
- `Graph` encapsulates the full dependency graph for a project
|
|
23
|
+
(including monorepo workspaces), and is the source of truth for how
|
|
24
|
+
to lay out `node_modules`.
|
|
25
|
+
- `Node` represents a unique package instance (uniqueness provided by
|
|
26
|
+
`@vltpkg/dep-id`).
|
|
27
|
+
- `Edge` represents a dependency relationship from a dependent to a
|
|
28
|
+
dependency (eg. `dependencies`, `devDependencies`,
|
|
29
|
+
`peerDependencies`, etc.).
|
|
30
|
+
- `Diff` describes the minimal set of changes required to transform an
|
|
31
|
+
Actual graph (disk) into an Ideal graph (desired outcome), which is
|
|
32
|
+
then applied by the `reify` subsystem.
|
|
33
|
+
|
|
34
|
+
## Concepts
|
|
35
|
+
|
|
36
|
+
- Importers: Root-level nodes used as starting points of the graph.
|
|
37
|
+
The `mainImporter` is the project root (its `package.json`), and the
|
|
38
|
+
remaining importers are workspaces discovered by
|
|
39
|
+
`@vltpkg/workspaces`.
|
|
40
|
+
- Hidden Lockfile: A performance optimization stored at
|
|
41
|
+
`node_modules/.vlt-lock.json` mirroring the current on-disk state to
|
|
42
|
+
accelerate subsequent loads of the Actual graph.
|
|
43
|
+
- Modifiers: Configuration for selectively altering dependency
|
|
44
|
+
resolution; Ideal/Actual builders support skipping node loads when
|
|
45
|
+
modifiers change.
|
|
9
46
|
|
|
10
47
|
## API
|
|
11
48
|
|
|
@@ -26,6 +63,13 @@ local file system.
|
|
|
26
63
|
|
|
27
64
|
Loads the lockfile file found at `projectRoot` and returns the graph.
|
|
28
65
|
|
|
66
|
+
### `reify(options): Promise<Diff>`
|
|
67
|
+
|
|
68
|
+
Computes a `Diff` between the Actual and Ideal graphs and applies the
|
|
69
|
+
minimal filesystem changes (creating/deleting links, writing
|
|
70
|
+
lockfiles, hoisting, lifecycle scripts) to make the on-disk install
|
|
71
|
+
match the Ideal graph.
|
|
72
|
+
|
|
29
73
|
## Usage
|
|
30
74
|
|
|
31
75
|
Here's a quick example of how to use the `@vltpkg/graph.ideal.build`
|
|
@@ -37,3 +81,94 @@ import { ideal } from '@vltpkg/graph'
|
|
|
37
81
|
|
|
38
82
|
const graph = await ideal.build({ projectRoot: process.cwd() })
|
|
39
83
|
```
|
|
84
|
+
|
|
85
|
+
### Load Actual Graph and Reify
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { actual, ideal, reify } from '@vltpkg/graph'
|
|
89
|
+
|
|
90
|
+
// Load current on-disk state
|
|
91
|
+
const from = actual.load({
|
|
92
|
+
projectRoot: process.cwd(),
|
|
93
|
+
packageJson,
|
|
94
|
+
scurry,
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// Build intended end state (may start from lockfile or actual)
|
|
98
|
+
const to = await ideal.build({
|
|
99
|
+
projectRoot: process.cwd(),
|
|
100
|
+
packageInfo,
|
|
101
|
+
packageJson,
|
|
102
|
+
scurry,
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// Apply minimal changes to match Ideal
|
|
106
|
+
await reify({
|
|
107
|
+
graph: to,
|
|
108
|
+
actual: from,
|
|
109
|
+
packageInfo,
|
|
110
|
+
packageJson,
|
|
111
|
+
scurry,
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Working With Lockfiles
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { lockfile } from '@vltpkg/graph'
|
|
119
|
+
|
|
120
|
+
// Load virtual graph from vlt-lock.json
|
|
121
|
+
const g = lockfile.load({
|
|
122
|
+
projectRoot,
|
|
123
|
+
mainManifest,
|
|
124
|
+
packageJson,
|
|
125
|
+
scurry,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Save both lockfile formats
|
|
129
|
+
lockfile.save({ graph: g, projectRoot, packageJson, scurry })
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Architecture
|
|
133
|
+
|
|
134
|
+
Graph construction modes supported by the library:
|
|
135
|
+
|
|
136
|
+
- Virtual Graphs (lockfile-based)
|
|
137
|
+
- Load and save via `src/lockfile/load.ts` and
|
|
138
|
+
`src/lockfile/save.ts`
|
|
139
|
+
- Hidden lockfile: `node_modules/.vlt-lock.json` for faster loads
|
|
140
|
+
|
|
141
|
+
- Actual Graphs (filesystem-based)
|
|
142
|
+
- Loaded by traversing `node_modules` via `src/actual/load.ts`
|
|
143
|
+
- May shortcut to Hidden Lockfile if present and valid
|
|
144
|
+
- File layout changes are performed by `src/reify/`
|
|
145
|
+
|
|
146
|
+
- Ideal Graphs (desired end state)
|
|
147
|
+
- Entry: `src/ideal/build.ts`
|
|
148
|
+
- Starts from Virtual (preferred) or falls back to Actual
|
|
149
|
+
- Merges `add`/`remove` input with importer manifests using
|
|
150
|
+
`src/ideal/get-importer-specs.ts`
|
|
151
|
+
- Fetches and expands manifests using `@vltpkg/package-info`, reuses
|
|
152
|
+
existing nodes that satisfy specs
|
|
153
|
+
|
|
154
|
+
Finally, `src/diff.ts` computes changes and `src/reify/` applies them
|
|
155
|
+
to the filesystem.
|
|
156
|
+
|
|
157
|
+
## Related Workspaces
|
|
158
|
+
|
|
159
|
+
- `@vltpkg/dep-id`: Unique IDs for packages, ensuring `Node` identity
|
|
160
|
+
- `@vltpkg/spec`: Parse/normalize dependency specifiers and registry
|
|
161
|
+
semantics
|
|
162
|
+
- `@vltpkg/semver`: Semantic version parsing/comparison
|
|
163
|
+
- `@vltpkg/package-info`: Fetch remote manifests and artifacts
|
|
164
|
+
(registry, git, tarball)
|
|
165
|
+
- `@vltpkg/package-json`: Read and cache local `package.json` files
|
|
166
|
+
- `@vltpkg/workspaces`: Monorepo workspace discovery and grouping
|
|
167
|
+
|
|
168
|
+
## References
|
|
169
|
+
|
|
170
|
+
- package.json format and behavior:
|
|
171
|
+
[https://docs.npmjs.com/cli/v11/configuring-npm/package-json](https://docs.npmjs.com/cli/v11/configuring-npm/package-json)
|
|
172
|
+
- Semantic Versioning:
|
|
173
|
+
[https://semver.org/spec/v2.0.0.html](https://semver.org/spec/v2.0.0.html)
|
|
174
|
+
- Monorepos: [https://monorepo.tools](https://monorepo.tools)
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { Spec } from '@vltpkg/spec';
|
|
2
|
+
import { Graph } from '../graph.ts';
|
|
3
|
+
import type { DepID } from '@vltpkg/dep-id';
|
|
1
4
|
import type { PackageJson } from '@vltpkg/package-json';
|
|
2
5
|
import type { SpecOptions } from '@vltpkg/spec';
|
|
3
|
-
import type {
|
|
6
|
+
import type { NormalizedManifest } from '@vltpkg/types';
|
|
4
7
|
import type { Monorepo } from '@vltpkg/workspaces';
|
|
5
8
|
import type { Path, PathScurry } from 'path-scurry';
|
|
6
|
-
import {
|
|
9
|
+
import type { GraphModifier } from '../modifiers.ts';
|
|
7
10
|
export type LoadOptions = SpecOptions & {
|
|
8
11
|
/**
|
|
9
12
|
* The project root dirname.
|
|
@@ -12,7 +15,11 @@ export type LoadOptions = SpecOptions & {
|
|
|
12
15
|
/**
|
|
13
16
|
* The project root manifest.
|
|
14
17
|
*/
|
|
15
|
-
mainManifest?:
|
|
18
|
+
mainManifest?: NormalizedManifest;
|
|
19
|
+
/**
|
|
20
|
+
* The graph modifiers helper object.
|
|
21
|
+
*/
|
|
22
|
+
modifiers?: GraphModifier;
|
|
16
23
|
/**
|
|
17
24
|
* A {@link Monorepo} object, for managing workspaces
|
|
18
25
|
*/
|
|
@@ -39,12 +46,51 @@ export type LoadOptions = SpecOptions & {
|
|
|
39
46
|
* hidden lockfile at `node_modules/.vlt-lock.json`
|
|
40
47
|
*/
|
|
41
48
|
skipHiddenLockfile?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Load only importers into the graph if the modifiers have changed.
|
|
51
|
+
*/
|
|
52
|
+
skipLoadingNodesOnModifiersChange?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* If set to `true`, fail if lockfile is missing or out of date.
|
|
55
|
+
* Used by ci command to enforce lockfile integrity.
|
|
56
|
+
*/
|
|
57
|
+
expectLockfile?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* If set to `true`, fail if lockfile is missing or out of sync with package.json.
|
|
60
|
+
* Prevents any lockfile modifications and is stricter than expectLockfile.
|
|
61
|
+
*/
|
|
62
|
+
frozenLockfile?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* If set to `true`, only update the lockfile without performing any node_modules
|
|
65
|
+
* operations. Skips package extraction, filesystem operations, and hidden lockfile saves.
|
|
66
|
+
*/
|
|
67
|
+
lockfileOnly?: boolean;
|
|
42
68
|
};
|
|
43
69
|
export type ReadEntry = {
|
|
44
70
|
alias: string;
|
|
45
71
|
name: string;
|
|
46
72
|
realpath: Path;
|
|
47
73
|
};
|
|
74
|
+
/**
|
|
75
|
+
* The configuration object type as it is saved in the `.vlt/vlt.json`
|
|
76
|
+
*/
|
|
77
|
+
export type StoreConfigObject = {
|
|
78
|
+
modifiers: Record<string, string> | undefined;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Checks if a given object is a {@link StoreConfigObject}.
|
|
82
|
+
*/
|
|
83
|
+
export declare const isStoreConfigObject: (obj: unknown) => obj is StoreConfigObject;
|
|
84
|
+
/**
|
|
85
|
+
* Returns a {@link StoreConfigObject} from a given object.
|
|
86
|
+
* Throws a TypeError if the object can't be converted.
|
|
87
|
+
*/
|
|
88
|
+
export declare const asStoreConfigObject: (obj: unknown) => StoreConfigObject;
|
|
89
|
+
/**
|
|
90
|
+
* Returns a {@link DepID} for a given spec and path, if the spec is
|
|
91
|
+
* path-based or a registry spec, otherwise returns `undefined`.
|
|
92
|
+
*/
|
|
93
|
+
export declare const getPathBasedId: (spec: Spec, path: Path) => DepID | undefined;
|
|
48
94
|
/**
|
|
49
95
|
* Read the file system looking for `node_modules` folders and
|
|
50
96
|
* returns a new {@link Graph} that represents the relationship
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../../src/actual/load.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../../src/actual/load.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAQnC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAGnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEnD,OAAO,KAAK,EACV,aAAa,EAEd,MAAM,iBAAiB,CAAA;AAGxB,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG;IACtC;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,aAAa,CAAA;IACzB;;OAEG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB;;OAEG;IACH,WAAW,EAAE,WAAW,CAAA;IACxB;;OAEG;IACH,MAAM,EAAE,UAAU,CAAA;IAClB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;OAEG;IACH,iCAAiC,CAAC,EAAE,OAAO,CAAA;IAE3C;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,IAAI,CAAA;CACf,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;CAC9C,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,QACzB,OAAO,KACX,GAAG,IAAI,iBAGe,CAAA;AAEzB;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QACzB,OAAO,KACX,iBAKF,CAAA;AAUD;;;GAGG;AACH,eAAO,MAAM,cAAc,SACnB,IAAI,QACJ,IAAI,KACT,KAAK,GAAG,SAGQ,CAAA;AA2SnB;;;;GAIG;AACH,eAAO,MAAM,IAAI,YAAa,WAAW,KAAG,KAsG3C,CAAA"}
|
package/dist/esm/actual/load.js
CHANGED
|
@@ -1,15 +1,40 @@
|
|
|
1
|
-
import { asDepID, hydrate, joinDepIDTuple } from '@vltpkg/dep-id';
|
|
1
|
+
import { asDepID, hydrate, joinDepIDTuple, joinExtra, splitDepID, splitExtra, } from '@vltpkg/dep-id';
|
|
2
2
|
import { Spec } from '@vltpkg/spec';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { graphStep } from '@vltpkg/output';
|
|
4
|
+
import { isObject } from '@vltpkg/types';
|
|
5
|
+
import { shorten, getRawDependencies, getDependencies, } from "../dependencies.js";
|
|
5
6
|
import { Graph } from "../graph.js";
|
|
6
7
|
import { loadHidden } from "../lockfile/load.js";
|
|
7
|
-
import {
|
|
8
|
+
import { saveHidden } from "../lockfile/save.js";
|
|
9
|
+
import { readFileSync } from 'node:fs';
|
|
10
|
+
/**
|
|
11
|
+
* Checks if a given object is a {@link StoreConfigObject}.
|
|
12
|
+
*/
|
|
13
|
+
export const isStoreConfigObject = (obj) => isObject(obj) &&
|
|
14
|
+
Object.prototype.hasOwnProperty.call(obj, 'modifiers') &&
|
|
15
|
+
isObject(obj.modifiers);
|
|
16
|
+
/**
|
|
17
|
+
* Returns a {@link StoreConfigObject} from a given object.
|
|
18
|
+
* Throws a TypeError if the object can't be converted.
|
|
19
|
+
*/
|
|
20
|
+
export const asStoreConfigObject = (obj) => {
|
|
21
|
+
if (!isStoreConfigObject(obj)) {
|
|
22
|
+
throw new TypeError(`Expected a store config object, got ${obj}`);
|
|
23
|
+
}
|
|
24
|
+
return obj;
|
|
25
|
+
};
|
|
26
|
+
// path-based refer to the types of dependencies that are directly linked to
|
|
27
|
+
// their real location in the file system and thus will not have an entry
|
|
28
|
+
// in the `node_modules/.vlt` store
|
|
8
29
|
const pathBasedType = new Set(['file', 'workspace']);
|
|
9
30
|
const isPathBasedType = (type) => pathBasedType.has(type);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Returns a {@link DepID} for a given spec and path, if the spec is
|
|
33
|
+
* path-based or a registry spec, otherwise returns `undefined`.
|
|
34
|
+
*/
|
|
35
|
+
export const getPathBasedId = (spec, path) => isPathBasedType(spec.type) ?
|
|
36
|
+
joinDepIDTuple([spec.type, path.relativePosix()])
|
|
37
|
+
: findDepID(path);
|
|
13
38
|
/**
|
|
14
39
|
* Retrieve the {@link DepID} for a given package from its location.
|
|
15
40
|
*/
|
|
@@ -26,47 +51,25 @@ const findNodeModules = ({ parent, name, isCWD, }) => parent?.name === 'node_mod
|
|
|
26
51
|
* Retrieves the scoped-normalized package name from its {@link Path}.
|
|
27
52
|
*/
|
|
28
53
|
const findName = ({ parent, name }) => parent?.name.startsWith('@') ? `${parent.name}/${name}` : name;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*
|
|
32
|
-
*
|
|
54
|
+
/**
|
|
55
|
+
* Helper function that gets a modified {@link Spec} when finding a modifier
|
|
56
|
+
* that applies to a given dependency. Otherwise returns the original spec
|
|
57
|
+
* value and no queryModifier.
|
|
33
58
|
*/
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// only care about devDeps for importers and git or symlink deps
|
|
47
|
-
// technically this will also include devDeps for tarball file: specs,
|
|
48
|
-
// but that is likely rare enough to not worry about too much.
|
|
49
|
-
if (depType === 'devDependencies' &&
|
|
50
|
-
!node.importer &&
|
|
51
|
-
!node.id.startsWith('git') &&
|
|
52
|
-
!node.id.startsWith('file')) {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (obj) {
|
|
56
|
-
for (const [name, bareSpec] of Object.entries(obj)) {
|
|
57
|
-
// if it's a bundled dependency, we just ignore it entirely.
|
|
58
|
-
if (bundled.has(name))
|
|
59
|
-
continue;
|
|
60
|
-
dependencies.set(name, {
|
|
61
|
-
name,
|
|
62
|
-
type: depType,
|
|
63
|
-
bareSpec,
|
|
64
|
-
registry: node.registry,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
|
59
|
+
const maybeApplyModifierToSpec = (spec, depName, modifierRefs) => {
|
|
60
|
+
const activeModifier = modifierRefs?.get(depName);
|
|
61
|
+
const queryModifier = activeModifier?.modifier.query;
|
|
62
|
+
const completeModifier = activeModifier &&
|
|
63
|
+
activeModifier.interactiveBreadcrumb.current ===
|
|
64
|
+
activeModifier.modifier.breadcrumb.last;
|
|
65
|
+
if (queryModifier &&
|
|
66
|
+
completeModifier &&
|
|
67
|
+
'spec' in activeModifier.modifier) {
|
|
68
|
+
const modifiedSpec = activeModifier.modifier.spec;
|
|
69
|
+
modifiedSpec.overridden = true;
|
|
70
|
+
return { spec: modifiedSpec, queryModifier };
|
|
68
71
|
}
|
|
69
|
-
return
|
|
72
|
+
return { spec, queryModifier };
|
|
70
73
|
};
|
|
71
74
|
/**
|
|
72
75
|
* Reads the current directory defined at `currDir` and looks for folder
|
|
@@ -115,10 +118,14 @@ const readDir = (scurry, currDir, fromNodeName) => {
|
|
|
115
118
|
* as dependencies of `fromNode`, building the instantiated `graph`.
|
|
116
119
|
*/
|
|
117
120
|
const parseDir = (options, scurry, packageJson, depsFound, graph, fromNode, currDir) => {
|
|
118
|
-
const { loadManifests } = options;
|
|
119
|
-
const dependencies =
|
|
121
|
+
const { loadManifests, modifiers } = options;
|
|
122
|
+
const dependencies = getRawDependencies(fromNode);
|
|
120
123
|
const seenDeps = new Set();
|
|
121
124
|
const readItems = readDir(scurry, currDir, fromNode.name);
|
|
125
|
+
// Get modifier references for this node's dependencies
|
|
126
|
+
const modifierRefs = modifiers?.tryDependencies(fromNode, [
|
|
127
|
+
...getDependencies(fromNode, options).values(),
|
|
128
|
+
]);
|
|
122
129
|
for (const { alias, name, realpath } of readItems) {
|
|
123
130
|
let node;
|
|
124
131
|
// tracks what dependencies have been seen
|
|
@@ -129,10 +136,17 @@ const parseDir = (options, scurry, packageJson, depsFound, graph, fromNode, curr
|
|
|
129
136
|
if (!loadManifests) {
|
|
130
137
|
const depId = findDepID(realpath);
|
|
131
138
|
if (depId) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
let h = hydrate(depId, alias, options);
|
|
140
|
+
// if the parsed registry value is using the default value, then
|
|
141
|
+
// the node should inherit the registry value from its parent node
|
|
142
|
+
if (h.type === 'registry' &&
|
|
143
|
+
h.registry === h.options.registry &&
|
|
144
|
+
fromNode.registry) {
|
|
145
|
+
h.registry = fromNode.registry;
|
|
146
|
+
}
|
|
147
|
+
// Check for active modifiers and replace spec even when not loading manifests
|
|
148
|
+
const { spec: modifiedSpec, queryModifier } = maybeApplyModifierToSpec(h, alias, modifierRefs);
|
|
149
|
+
h = modifiedSpec;
|
|
136
150
|
// graphs build with no manifest have no notion of
|
|
137
151
|
// dependency types and or spec definitions since those
|
|
138
152
|
// would have to be parsed from a manifest
|
|
@@ -140,10 +154,12 @@ const parseDir = (options, scurry, packageJson, depsFound, graph, fromNode, curr
|
|
|
140
154
|
h, // uses spec from hydrated id
|
|
141
155
|
{
|
|
142
156
|
name,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
157
|
+
}, depId, joinExtra({ modifier: queryModifier }));
|
|
158
|
+
// Update active entry after placing package
|
|
159
|
+
const activeModifier = modifierRefs?.get(alias);
|
|
160
|
+
if (activeModifier && node) {
|
|
161
|
+
modifiers?.updateActiveEntry(node, activeModifier);
|
|
162
|
+
}
|
|
147
163
|
}
|
|
148
164
|
}
|
|
149
165
|
// retrieve references to the current folder name found in `fromNode`
|
|
@@ -161,12 +177,35 @@ const parseDir = (options, scurry, packageJson, depsFound, graph, fromNode, curr
|
|
|
161
177
|
const type = deps?.type || 'dependencies';
|
|
162
178
|
const bareSpec = deps?.bareSpec || '*';
|
|
163
179
|
const depType = shorten(type, alias, fromNode.manifest);
|
|
164
|
-
|
|
180
|
+
let spec = Spec.parse(alias, bareSpec, {
|
|
165
181
|
...options,
|
|
166
182
|
registry: fromNode.registry,
|
|
167
183
|
});
|
|
168
|
-
|
|
169
|
-
|
|
184
|
+
// Check for active modifiers and replace spec if a modifier is complete
|
|
185
|
+
const { spec: modifiedSpec, queryModifier } = maybeApplyModifierToSpec(spec, alias, modifierRefs);
|
|
186
|
+
spec = modifiedSpec;
|
|
187
|
+
const maybeId = getPathBasedId(spec, realpath);
|
|
188
|
+
let peerSetHash;
|
|
189
|
+
if (maybeId) {
|
|
190
|
+
// parses extra info from depID to retrieve peerSetHash
|
|
191
|
+
try {
|
|
192
|
+
const tuple = splitDepID(maybeId);
|
|
193
|
+
const type = tuple[0];
|
|
194
|
+
const extra = type === 'registry' || type === 'git' ?
|
|
195
|
+
tuple[3]
|
|
196
|
+
: tuple[2];
|
|
197
|
+
peerSetHash =
|
|
198
|
+
extra ? splitExtra(extra).peerSetHash : undefined;
|
|
199
|
+
/* c8 ignore next - impossible: getPathBasedId asserts valid dep id */
|
|
200
|
+
}
|
|
201
|
+
catch { }
|
|
202
|
+
}
|
|
203
|
+
node = graph.placePackage(fromNode, depType, spec, mani, maybeId, joinExtra({ modifier: queryModifier, peerSetHash }));
|
|
204
|
+
// Update active entry after placing package
|
|
205
|
+
const activeModifier = modifierRefs?.get(alias);
|
|
206
|
+
if (activeModifier && node) {
|
|
207
|
+
modifiers?.updateActiveEntry(node, activeModifier);
|
|
208
|
+
}
|
|
170
209
|
}
|
|
171
210
|
if (node) {
|
|
172
211
|
// If a found dependency is not declared in any of the original
|
|
@@ -197,11 +236,14 @@ const parseDir = (options, scurry, packageJson, depsFound, graph, fromNode, curr
|
|
|
197
236
|
for (const { name, type, bareSpec } of dependencies.values()) {
|
|
198
237
|
if (!seenDeps.has(name)) {
|
|
199
238
|
const depType = shorten(type, name, fromNode.manifest);
|
|
200
|
-
|
|
239
|
+
let spec = Spec.parse(name, bareSpec, {
|
|
201
240
|
...options,
|
|
202
241
|
registry: fromNode.registry,
|
|
203
242
|
});
|
|
204
|
-
|
|
243
|
+
// Check for active modifiers and replace spec for missing dependencies
|
|
244
|
+
const { spec: modifiedSpec, queryModifier } = maybeApplyModifierToSpec(spec, name, modifierRefs);
|
|
245
|
+
spec = modifiedSpec;
|
|
246
|
+
graph.placePackage(fromNode, depType, spec, undefined, undefined, joinExtra({ modifier: queryModifier }));
|
|
205
247
|
}
|
|
206
248
|
}
|
|
207
249
|
};
|
|
@@ -212,34 +254,64 @@ const parseDir = (options, scurry, packageJson, depsFound, graph, fromNode, curr
|
|
|
212
254
|
*/
|
|
213
255
|
export const load = (options) => {
|
|
214
256
|
const done = graphStep('actual');
|
|
215
|
-
|
|
216
|
-
const { skipHiddenLockfile = true, projectRoot, packageJson, scurry, monorepo, } = options;
|
|
257
|
+
const { modifiers, monorepo, projectRoot, packageJson, scurry, skipHiddenLockfile = false, skipLoadingNodesOnModifiersChange = false, } = options;
|
|
217
258
|
const mainManifest = options.mainManifest ?? packageJson.read(projectRoot);
|
|
218
259
|
if (!skipHiddenLockfile) {
|
|
219
260
|
try {
|
|
261
|
+
// if we reach here, the hidden lockfile is valid
|
|
220
262
|
const graph = loadHidden({
|
|
263
|
+
...options,
|
|
221
264
|
projectRoot,
|
|
222
265
|
mainManifest,
|
|
223
266
|
packageJson,
|
|
224
267
|
monorepo,
|
|
225
268
|
scurry,
|
|
226
269
|
});
|
|
227
|
-
|
|
270
|
+
done();
|
|
228
271
|
return graph;
|
|
229
272
|
}
|
|
230
|
-
catch {
|
|
273
|
+
catch {
|
|
274
|
+
// if validation fails or any other error occurs,
|
|
275
|
+
// fall back to filesystem traversal
|
|
276
|
+
}
|
|
231
277
|
}
|
|
232
278
|
const graph = new Graph({ ...options, mainManifest });
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
279
|
+
// retrieve the configuration object from the store
|
|
280
|
+
let storeConfig = { modifiers: undefined };
|
|
281
|
+
try {
|
|
282
|
+
storeConfig = asStoreConfigObject(JSON.parse(readFileSync(scurry.resolve('node_modules/.vlt/vlt.json'), 'utf8')));
|
|
237
283
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
284
|
+
catch { }
|
|
285
|
+
const storeModifiers = JSON.stringify(storeConfig.modifiers ?? {});
|
|
286
|
+
const optionsModifiers = JSON.stringify(modifiers?.config);
|
|
287
|
+
const modifiersChanged = storeModifiers !== optionsModifiers;
|
|
288
|
+
const shouldLoadDependencies = !(skipLoadingNodesOnModifiersChange && modifiersChanged);
|
|
289
|
+
// will only skip loading dependencies if the
|
|
290
|
+
// skipLoadingNodesOnModifiersChange option is set to true
|
|
291
|
+
// and the current modifiers have not changed when compared
|
|
292
|
+
// to the modifiers stored in the `node_modules/.vlt/vlt.json` store config
|
|
293
|
+
if (shouldLoadDependencies) {
|
|
294
|
+
const depsFound = new Map();
|
|
295
|
+
// starts the list of initial folders to parse using the importer nodes
|
|
296
|
+
for (const importer of graph.importers) {
|
|
297
|
+
modifiers?.tryImporter(importer);
|
|
298
|
+
depsFound.set(importer, scurry.cwd.resolve(`${importer.location}/node_modules`));
|
|
299
|
+
}
|
|
300
|
+
// breadth-first traversal of the file system tree reading deps found
|
|
301
|
+
// starting from the node_modules folder of every importer in order to
|
|
302
|
+
// find the actual installed dependencies at each location
|
|
303
|
+
for (const [node, path] of depsFound.entries()) {
|
|
304
|
+
parseDir(options, scurry, packageJson, depsFound, graph, node, path);
|
|
305
|
+
}
|
|
306
|
+
// Clean up any pending modifier entries that were never completed
|
|
307
|
+
modifiers?.rollbackActiveEntries();
|
|
308
|
+
// caches the load result to the hidden lockfile when enabled
|
|
309
|
+
if (scurry.cwd.resolve('node_modules').lstatSync()?.isDirectory()) {
|
|
310
|
+
saveHidden({
|
|
311
|
+
...options,
|
|
312
|
+
graph,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
243
315
|
}
|
|
244
316
|
done();
|
|
245
317
|
return graph;
|