zyrk 0.4.8 → 0.4.9
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 +5 -2
- package/VERSION +1 -1
- package/dist/install/import-npm-workspace.d.ts +5 -0
- package/dist/install/import-npm-workspace.d.ts.map +1 -0
- package/dist/install/import-npm-workspace.js +283 -0
- package/dist/install/workspace-install.d.ts.map +1 -1
- package/dist/install/workspace-install.js +37 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -141,8 +141,11 @@ zyrk add package-name # edit this importer, preserve other select
|
|
|
141
141
|
|
|
142
142
|
Frozen/offline installs, live peers, patches, mutable registry packages, inspection,
|
|
143
143
|
and repair have workspace fixtures. Builds that reach live workspace sources are
|
|
144
|
-
blocked before execution.
|
|
145
|
-
|
|
144
|
+
blocked before execution. Existing npm v2/v3 workspace `package-lock.json` selections can be adopted with
|
|
145
|
+
`zyrk import --from npm`, preserving exact versions, aliases and live members.
|
|
146
|
+
Adoption validates the complete graph before project changes; npm shrinkwrap
|
|
147
|
+
precedence and unsupported bundled/external links fail explicitly. Portable
|
|
148
|
+
workspace export still needs its adapter. Recursive execution supports
|
|
146
149
|
member scripts; filters are exact names or paths, not arbitrary glob expressions.
|
|
147
150
|
|
|
148
151
|
### Run a one-off tool
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v0.4.
|
|
1
|
+
v0.4.9
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { NpmLockDoc } from "../lockfile/types.ts";
|
|
2
|
+
import type { WorkspaceProject } from "./workspaces.ts";
|
|
3
|
+
import type { WorkspaceGraph } from "./workspace-graph.ts";
|
|
4
|
+
export declare function importNpmWorkspaceLock(doc: NpmLockDoc, project: WorkspaceProject, origin: string): WorkspaceGraph;
|
|
5
|
+
//# sourceMappingURL=import-npm-workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-npm-workspace.d.ts","sourceRoot":"","sources":["../../src/install/import-npm-workspace.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAQvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,sBAAsB,CAAC;AAE9B,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,MAAM,GACb,cAAc,CAyThB"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/** Translate npm's physical virtual tree without selecting new registry versions. */
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { satisfies } from "semver";
|
|
4
|
+
import { validateLockDoc } from "../lockfile/read.js";
|
|
5
|
+
import { sha512Hex } from "../util/hash.js";
|
|
6
|
+
import { canonicalJson } from "../util/json.js";
|
|
7
|
+
import { importNpmLock } from "./import-npm.js";
|
|
8
|
+
import { lockMismatch, cleanUrl } from "./lock.js";
|
|
9
|
+
import { ZyrkError } from "../errors.js";
|
|
10
|
+
import { assertWorkspacePlatform } from "./workspace-graph.js";
|
|
11
|
+
export function importNpmWorkspaceLock(doc, project, origin) {
|
|
12
|
+
if (!doc ||
|
|
13
|
+
typeof doc !== "object" ||
|
|
14
|
+
Array.isArray(doc) ||
|
|
15
|
+
!doc.packages ||
|
|
16
|
+
typeof doc.packages !== "object" ||
|
|
17
|
+
Array.isArray(doc.packages) ||
|
|
18
|
+
Object.values(doc.packages).some((e) => !e || typeof e !== "object" || Array.isArray(e)))
|
|
19
|
+
throw lockMismatch("npm workspace lock has malformed package entries");
|
|
20
|
+
const portable = structuredClone(doc);
|
|
21
|
+
// Relative file declarations are representable only when they name a
|
|
22
|
+
// discovered live importer. Validate their targets before URL validation.
|
|
23
|
+
const importerIds = new Set(project.importers.map((i) => (i.id === "." ? "" : i.id)));
|
|
24
|
+
for (const [location, entry] of Object.entries(portable.packages ?? {}))
|
|
25
|
+
for (const group of [
|
|
26
|
+
"dependencies",
|
|
27
|
+
"devDependencies",
|
|
28
|
+
"optionalDependencies",
|
|
29
|
+
"peerDependencies",
|
|
30
|
+
])
|
|
31
|
+
if (entry[group] && typeof entry[group] === "object")
|
|
32
|
+
for (const [name, spec] of Object.entries(entry[group]))
|
|
33
|
+
if (typeof spec === "string" && spec.startsWith("file:")) {
|
|
34
|
+
const relative = spec.slice(5);
|
|
35
|
+
if (!importerIds.has(location) ||
|
|
36
|
+
relative.startsWith("/") ||
|
|
37
|
+
relative.includes("\\") ||
|
|
38
|
+
/^[A-Za-z]:/.test(relative) ||
|
|
39
|
+
!importerIds.has(path.posix.normalize(path.posix.join(location, relative))))
|
|
40
|
+
throw lockMismatch("npm file declaration is not a declared workspace");
|
|
41
|
+
entry[group][name] = "workspace:*";
|
|
42
|
+
}
|
|
43
|
+
// npm v2 also has a redundant legacy dependency view. Do not adopt a
|
|
44
|
+
// second authority; it is still scanned for unsafe metadata.
|
|
45
|
+
const sanitizeLegacyLinks = (value) => {
|
|
46
|
+
if (typeof value === "string" &&
|
|
47
|
+
value.startsWith("file:") &&
|
|
48
|
+
importerIds.has(value.slice(5)))
|
|
49
|
+
return "workspace:*";
|
|
50
|
+
if (Array.isArray(value))
|
|
51
|
+
return value.map(sanitizeLegacyLinks);
|
|
52
|
+
if (value && typeof value === "object")
|
|
53
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [
|
|
54
|
+
key,
|
|
55
|
+
sanitizeLegacyLinks(child),
|
|
56
|
+
]));
|
|
57
|
+
return value;
|
|
58
|
+
};
|
|
59
|
+
if (portable.dependencies)
|
|
60
|
+
portable.dependencies = sanitizeLegacyLinks(portable.dependencies);
|
|
61
|
+
validateLockDoc(portable, "package-lock.json");
|
|
62
|
+
if (doc.zyrk ||
|
|
63
|
+
![2, 3].includes(doc.lockfileVersion ?? 0) ||
|
|
64
|
+
!doc.packages ||
|
|
65
|
+
!cleanUrl(origin))
|
|
66
|
+
throw lockMismatch("workspace adoption needs an unannotated npm v2/v3 lock");
|
|
67
|
+
const packages = doc.packages;
|
|
68
|
+
const graph = { nodes: [], importers: {}, omissions: [] };
|
|
69
|
+
const locations = new Map();
|
|
70
|
+
const local = new Map(project.importers.map((i) => [i.id === "." ? "" : i.id, i]));
|
|
71
|
+
for (const [location, entry] of Object.entries(packages)) {
|
|
72
|
+
if (location.includes("\\") ||
|
|
73
|
+
location.startsWith("/") ||
|
|
74
|
+
location.split("/").some((s) => s === ".." || s === ".") ||
|
|
75
|
+
location.includes("\0"))
|
|
76
|
+
throw lockMismatch("unsafe npm package location");
|
|
77
|
+
if (entry.link === true &&
|
|
78
|
+
(typeof entry.resolved !== "string" ||
|
|
79
|
+
!local.has(entry.resolved) ||
|
|
80
|
+
entry.resolved === ""))
|
|
81
|
+
throw lockMismatch("npm link does not select a declared workspace");
|
|
82
|
+
if (entry.inBundle === true)
|
|
83
|
+
throw lockMismatch("bundled npm selections are unsupported");
|
|
84
|
+
}
|
|
85
|
+
for (const [location, importer] of local) {
|
|
86
|
+
const entry = packages[location];
|
|
87
|
+
if (!entry || entry.link)
|
|
88
|
+
throw lockMismatch("npm lock lacks a workspace importer");
|
|
89
|
+
for (const field of [
|
|
90
|
+
"dependencies",
|
|
91
|
+
"devDependencies",
|
|
92
|
+
"optionalDependencies",
|
|
93
|
+
"peerDependencies",
|
|
94
|
+
"peerDependenciesMeta",
|
|
95
|
+
"workspaces",
|
|
96
|
+
])
|
|
97
|
+
if (canonicalJson(entry[field] ?? {}) !==
|
|
98
|
+
canonicalJson(importer.manifest[field] ?? {}))
|
|
99
|
+
throw lockMismatch("npm workspace manifest declarations differ");
|
|
100
|
+
if (location &&
|
|
101
|
+
((entry.name !== undefined && entry.name !== importer.manifest.name) ||
|
|
102
|
+
entry.version !== importer.manifest.version))
|
|
103
|
+
throw lockMismatch("npm workspace identity differs");
|
|
104
|
+
const node = {
|
|
105
|
+
kind: "local",
|
|
106
|
+
key: `workspace:${importer.id}`,
|
|
107
|
+
name: importer.manifest.name ?? "workspace-root",
|
|
108
|
+
version: importer.manifest.version ?? "0.0.0",
|
|
109
|
+
importer: importer.id,
|
|
110
|
+
edges: [],
|
|
111
|
+
peers: {},
|
|
112
|
+
optionalPeers: [],
|
|
113
|
+
};
|
|
114
|
+
graph.importers[importer.id] = node.key;
|
|
115
|
+
graph.nodes.push(node);
|
|
116
|
+
locations.set(location, node);
|
|
117
|
+
}
|
|
118
|
+
const find = (from, name) => {
|
|
119
|
+
let current = from;
|
|
120
|
+
for (;;) {
|
|
121
|
+
if (!current.endsWith("node_modules")) {
|
|
122
|
+
const slot = `${current ? current + "/" : ""}node_modules/${name}`;
|
|
123
|
+
if (Object.hasOwn(packages, slot))
|
|
124
|
+
return slot;
|
|
125
|
+
}
|
|
126
|
+
if (!current)
|
|
127
|
+
return undefined;
|
|
128
|
+
current = current.includes("/")
|
|
129
|
+
? current.slice(0, current.lastIndexOf("/"))
|
|
130
|
+
: "";
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const populated = new Set();
|
|
134
|
+
const load = (location, name, spec, from) => {
|
|
135
|
+
const entry = packages[location];
|
|
136
|
+
if (entry.link === true) {
|
|
137
|
+
const target = locations.get(entry.resolved);
|
|
138
|
+
const range = spec.startsWith("workspace:") ? spec.slice(10) : spec;
|
|
139
|
+
if (spec.startsWith("file:")
|
|
140
|
+
? path.posix.normalize(path.posix.join(from, spec.slice(5))) !==
|
|
141
|
+
(target.kind === "local" ? target.importer : "")
|
|
142
|
+
: name !== target.name ||
|
|
143
|
+
(!["*", "^", "~"].includes(range) &&
|
|
144
|
+
!satisfies(target.version, range)))
|
|
145
|
+
throw lockMismatch("npm workspace link disagrees with requirement");
|
|
146
|
+
return target;
|
|
147
|
+
}
|
|
148
|
+
// Reuse the strict single-selection adapter's archive, alias, source and
|
|
149
|
+
// platform validation. Dependencies are wired below using npm locations.
|
|
150
|
+
const shallow = { ...entry };
|
|
151
|
+
delete shallow.dependencies;
|
|
152
|
+
delete shallow.optionalDependencies;
|
|
153
|
+
delete shallow.peerDependencies;
|
|
154
|
+
const selected = importNpmLock({
|
|
155
|
+
lockfileVersion: 3,
|
|
156
|
+
packages: { "": {}, [`node_modules/${name}`]: shallow },
|
|
157
|
+
}, { [name]: spec }, origin).nodes[0];
|
|
158
|
+
let node = locations.get(location);
|
|
159
|
+
if (!node) {
|
|
160
|
+
const { peerBindings: _bindings, ...source } = selected;
|
|
161
|
+
node = {
|
|
162
|
+
...source,
|
|
163
|
+
key: `${selected.name}@${selected.version}#${sha512Hex(location).slice(0, 16)}`,
|
|
164
|
+
kind: "registry",
|
|
165
|
+
edges: [],
|
|
166
|
+
peers: {},
|
|
167
|
+
optionalPeers: [],
|
|
168
|
+
};
|
|
169
|
+
assertWorkspacePlatform({ nodes: [node], importers: {}, omissions: [] });
|
|
170
|
+
graph.nodes.push(node);
|
|
171
|
+
locations.set(location, node);
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
populate(location, node);
|
|
175
|
+
}
|
|
176
|
+
catch (cause) {
|
|
177
|
+
populated.delete(location);
|
|
178
|
+
locations.delete(location);
|
|
179
|
+
graph.nodes = graph.nodes.filter((n) => n !== node);
|
|
180
|
+
throw cause;
|
|
181
|
+
}
|
|
182
|
+
return node;
|
|
183
|
+
};
|
|
184
|
+
const populate = (location, node) => {
|
|
185
|
+
if (populated.has(location))
|
|
186
|
+
return;
|
|
187
|
+
if (populated.size >= 10000)
|
|
188
|
+
throw lockMismatch("npm import resource limit exceeded");
|
|
189
|
+
populated.add(location);
|
|
190
|
+
const entry = packages[location];
|
|
191
|
+
const requirements = new Map();
|
|
192
|
+
for (const [field, kind] of [
|
|
193
|
+
["dependencies", "prod"],
|
|
194
|
+
["devDependencies", "dev"],
|
|
195
|
+
["optionalDependencies", "optional"],
|
|
196
|
+
["peerDependencies", "peer"],
|
|
197
|
+
]) {
|
|
198
|
+
if (kind === "dev" && node.kind !== "local")
|
|
199
|
+
continue;
|
|
200
|
+
const values = entry[field];
|
|
201
|
+
if (values === undefined)
|
|
202
|
+
continue;
|
|
203
|
+
if (!values || typeof values !== "object" || Array.isArray(values))
|
|
204
|
+
throw lockMismatch("invalid npm dependency declarations");
|
|
205
|
+
for (const [name, spec] of Object.entries(values)) {
|
|
206
|
+
if (typeof spec !== "string")
|
|
207
|
+
throw lockMismatch("invalid npm dependency requirement");
|
|
208
|
+
// A peer declaration does not replace an existing regular dependency.
|
|
209
|
+
if (kind !== "peer" || !requirements.has(name))
|
|
210
|
+
requirements.set(name, { spec, kind });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
for (const [name, requirement] of requirements) {
|
|
214
|
+
const meta = entry.peerDependenciesMeta;
|
|
215
|
+
const optionalPeer = requirement.kind === "peer" && meta?.[name]?.optional === true;
|
|
216
|
+
if (optionalPeer)
|
|
217
|
+
node.optionalPeers.push(name);
|
|
218
|
+
const targetLocation = find(location, name);
|
|
219
|
+
if (targetLocation === undefined) {
|
|
220
|
+
if (optionalPeer) {
|
|
221
|
+
node.peers[name] = null;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (requirement.kind === "optional")
|
|
225
|
+
continue;
|
|
226
|
+
throw lockMismatch("npm workspace lock is missing a required selection");
|
|
227
|
+
}
|
|
228
|
+
let target;
|
|
229
|
+
try {
|
|
230
|
+
target = load(targetLocation, name, requirement.spec, location);
|
|
231
|
+
}
|
|
232
|
+
catch (cause) {
|
|
233
|
+
if (requirement.kind === "optional" &&
|
|
234
|
+
cause instanceof ZyrkError &&
|
|
235
|
+
cause.code === "ZYRK_PLATFORM_UNSUPPORTED") {
|
|
236
|
+
graph.omissions.push({ name, parent: node.key, reason: "platform" });
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
throw cause;
|
|
240
|
+
}
|
|
241
|
+
node.edges.push({ name, target: target.key, ...requirement });
|
|
242
|
+
if (requirement.kind === "peer")
|
|
243
|
+
node.peers[name] = target.key;
|
|
244
|
+
}
|
|
245
|
+
// npm allows the same name in dependencies and peerDependencies. Its
|
|
246
|
+
// ordinary edge keeps its kind, but the peer range and binding still apply.
|
|
247
|
+
const peers = entry.peerDependencies;
|
|
248
|
+
const meta = entry.peerDependenciesMeta;
|
|
249
|
+
for (const [name, spec] of Object.entries(peers ?? {})) {
|
|
250
|
+
if (Object.hasOwn(node.peers, name))
|
|
251
|
+
continue;
|
|
252
|
+
const optional = meta?.[name]?.optional === true;
|
|
253
|
+
if (optional)
|
|
254
|
+
node.optionalPeers.push(name);
|
|
255
|
+
const targetLocation = find(location, name);
|
|
256
|
+
if (targetLocation === undefined) {
|
|
257
|
+
if (!optional)
|
|
258
|
+
throw lockMismatch("npm workspace lock is missing a required peer");
|
|
259
|
+
node.peers[name] = null;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const target = load(targetLocation, name, spec, location);
|
|
263
|
+
node.peers[name] = target.key;
|
|
264
|
+
if (!node.edges.some((edge) => edge.name === name))
|
|
265
|
+
node.edges.push({ name, target: target.key, kind: "peer", spec });
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
for (const [location] of local)
|
|
269
|
+
populate(location, locations.get(location));
|
|
270
|
+
const reachable = new Set(), pending = Object.values(graph.importers), byKey = new Map(graph.nodes.map((n) => [n.key, n]));
|
|
271
|
+
while (pending.length) {
|
|
272
|
+
const key = pending.pop();
|
|
273
|
+
if (reachable.has(key))
|
|
274
|
+
continue;
|
|
275
|
+
reachable.add(key);
|
|
276
|
+
for (const edge of byKey.get(key).edges)
|
|
277
|
+
pending.push(edge.target);
|
|
278
|
+
}
|
|
279
|
+
graph.nodes = graph.nodes.filter((n) => reachable.has(n.key));
|
|
280
|
+
graph.omissions = graph.omissions.filter((o) => reachable.has(o.parent));
|
|
281
|
+
graph.nodes.sort((a, b) => a.key.localeCompare(b.key));
|
|
282
|
+
return graph;
|
|
283
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-install.d.ts","sourceRoot":"","sources":["../../src/install/workspace-install.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace-install.d.ts","sourceRoot":"","sources":["../../src/install/workspace-install.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAyH9D,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAopBxB"}
|
|
@@ -4,6 +4,7 @@ import { constants } from "node:fs";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { discoverWorkspaces, findWorkspaceRoot, assertNoLiveSourceBuilds, } from "./workspaces.js";
|
|
6
6
|
import { resolveWorkspaceGraph, workspaceRegistryOccurrences, workspaceContextId, assertWorkspacePlatform, } from "./workspace-graph.js";
|
|
7
|
+
import { importNpmWorkspaceLock } from "./import-npm-workspace.js";
|
|
7
8
|
import { readWorkspaceLock, workspaceLockDoc } from "./workspace-lock.js";
|
|
8
9
|
import { publishWorkspaceProjection, recoverWorkspaceProjection, workspaceProjectionPaths, } from "./workspace-transaction.js";
|
|
9
10
|
import { ZyrkError } from "../errors.js";
|
|
@@ -48,10 +49,40 @@ async function realDirectory(directory) {
|
|
|
48
49
|
if (!st.isDirectory() || st.isSymbolicLink())
|
|
49
50
|
throw lockMismatch("workspace control parent must be a real directory");
|
|
50
51
|
}
|
|
52
|
+
async function readNpmWorkspaceImport(root, project, options) {
|
|
53
|
+
if (options.frozen || options.offline)
|
|
54
|
+
throw lockMismatch("npm workspace adoption needs a non-frozen online install");
|
|
55
|
+
if (await pathExists(path.join(root, "npm-shrinkwrap.json")))
|
|
56
|
+
throw lockMismatch("npm-shrinkwrap.json takes npm precedence; workspace adoption currently supports package-lock.json only");
|
|
57
|
+
let npmDoc;
|
|
58
|
+
try {
|
|
59
|
+
npmDoc = JSON.parse(await readFile(path.join(root, "package-lock.json"), "utf8"));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
throw lockMismatch("npm workspace adoption needs an existing readable JSON lock");
|
|
63
|
+
}
|
|
64
|
+
const graph = importNpmWorkspaceLock(npmDoc, project, options.registry.origin);
|
|
65
|
+
// Prove the complete graph and ordinary npm projection are representable
|
|
66
|
+
// before any project state is created. Actual policy fingerprints are added
|
|
67
|
+
// under the install lock after policy validation.
|
|
68
|
+
workspaceLockDoc(project, graph, {
|
|
69
|
+
manifest: project.manifestFingerprint,
|
|
70
|
+
scripts: scriptPolicyFingerprint(undefined),
|
|
71
|
+
config: configFingerprint({
|
|
72
|
+
registry: options.registry.origin,
|
|
73
|
+
linker: "shared",
|
|
74
|
+
}),
|
|
75
|
+
});
|
|
76
|
+
return graph;
|
|
77
|
+
}
|
|
51
78
|
export async function installWorkspaceProject(options) {
|
|
52
79
|
const root = await findWorkspaceRoot(options.projectDir);
|
|
53
80
|
if (!root)
|
|
54
81
|
throw lockMismatch("no authoritative workspace root");
|
|
82
|
+
// Refuse untranslatable locks before creating control directories or
|
|
83
|
+
// recovering a previous generation. Revalidate under the project lock below.
|
|
84
|
+
if (options.importNpm)
|
|
85
|
+
await readNpmWorkspaceImport(root, await discoverWorkspaces(root), options);
|
|
55
86
|
await realDirectory(path.join(root, ".zyrk-local"));
|
|
56
87
|
const held = await acquireLock(path.join(root, ".zyrk-local/project.lock"), {
|
|
57
88
|
name: "workspace install",
|
|
@@ -66,8 +97,6 @@ export async function installWorkspaceProject(options) {
|
|
|
66
97
|
i.id,
|
|
67
98
|
await readFile(path.join(i.directory, "package.json"), "utf8"),
|
|
68
99
|
])));
|
|
69
|
-
if (options.importNpm)
|
|
70
|
-
unsupported("Workspace npm-lock adoption needs its multi-importer adapter.");
|
|
71
100
|
const mutation = await prepareWorkspaceMutation(project, options);
|
|
72
101
|
if (manifest.overrides !== undefined ||
|
|
73
102
|
manifest.bundledDependencies !== undefined ||
|
|
@@ -110,7 +139,12 @@ export async function installWorkspaceProject(options) {
|
|
|
110
139
|
...(patchDigest ? { patches: patchDigest } : {}),
|
|
111
140
|
}),
|
|
112
141
|
};
|
|
113
|
-
|
|
142
|
+
const imported = options.importNpm
|
|
143
|
+
? await readNpmWorkspaceImport(root, project, options)
|
|
144
|
+
: undefined;
|
|
145
|
+
let existing = options.importNpm
|
|
146
|
+
? null
|
|
147
|
+
: await readWorkspaceLock(root, project, options.registry.origin), graph = imported;
|
|
114
148
|
if (existing) {
|
|
115
149
|
const ext = existing.doc.zyrk;
|
|
116
150
|
if (!mutation.changed &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zyrk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"description": "Storage-efficient Node package manager: one verified store per user, shared across projects through links.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@npmcli/arborist": "10.0.3",
|
|
35
|
-
"@types/node": "^
|
|
35
|
+
"@types/node": "^26.6.5",
|
|
36
36
|
"@types/semver": "^7.8.0",
|
|
37
37
|
"prettier": "3.9.10",
|
|
38
38
|
"typescript": "^7.0.2",
|