wyvrnpm 2.0.4 → 2.3.2
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 +639 -5
- package/bin/wyvrn.js +220 -10
- package/claude/skills/wyvrnpm.skill +0 -0
- package/cmake/cpp.cmake +9 -0
- package/cmake/functions.cmake +224 -0
- package/cmake/macros.cmake +233 -0
- package/cmake/options.cmake +23 -0
- package/cmake/variables.cmake +171 -0
- package/package.json +3 -1
- package/src/build/cache.js +148 -0
- package/src/build/clone.js +170 -0
- package/src/build/cmake.js +342 -0
- package/src/build/index.js +275 -0
- package/src/build/msvc-env.js +217 -0
- package/src/build/recipe.js +155 -0
- package/src/commands/build.js +283 -0
- package/src/commands/clean.js +56 -16
- package/src/commands/configure.js +6 -5
- package/src/commands/init.js +3 -2
- package/src/commands/install-skill.js +107 -0
- package/src/commands/install.js +262 -19
- package/src/commands/link.js +18 -15
- package/src/commands/profile.js +15 -12
- package/src/commands/publish.js +216 -65
- package/src/commands/show.js +237 -0
- package/src/compat.js +261 -0
- package/src/config.js +3 -1
- package/src/download.js +431 -87
- package/src/ignore.js +118 -0
- package/src/logger.js +94 -0
- package/src/manifest.js +12 -7
- package/src/options.js +303 -0
- package/src/profile.js +56 -4
- package/src/providers/base.js +16 -1
- package/src/providers/file.js +12 -6
- package/src/providers/http.js +15 -10
- package/src/providers/s3.js +14 -9
- package/src/resolve.js +179 -19
- package/src/toolchain/deps.js +164 -0
- package/src/toolchain/index.js +141 -0
- package/src/toolchain/presets.js +263 -0
- package/src/toolchain/template.cmake +66 -0
- package/src/upload-built.js +256 -0
- package/src/version-range.js +301 -0
package/src/commands/install.js
CHANGED
|
@@ -8,6 +8,19 @@ const { resolveDependencies } = require('../resolv
|
|
|
8
8
|
const { downloadDependencies } = require('../download');
|
|
9
9
|
const { readConfig } = require('../config');
|
|
10
10
|
const { loadProfile, hashProfile, formatProfile, mergeProfile } = require('../profile');
|
|
11
|
+
const { generateToolchain, generateCMakePresets } = require('../toolchain');
|
|
12
|
+
const { normalizeRecipe, hasBuildRecipe } = require('../build/recipe');
|
|
13
|
+
const {
|
|
14
|
+
normalizeOptionsDeclaration,
|
|
15
|
+
resolveEffectiveOptions,
|
|
16
|
+
parseCliOptions,
|
|
17
|
+
} = require('../options');
|
|
18
|
+
const {
|
|
19
|
+
resolveUploadDestination,
|
|
20
|
+
createUploadStats,
|
|
21
|
+
formatUploadSummary,
|
|
22
|
+
} = require('../upload-built');
|
|
23
|
+
const log = require('../logger');
|
|
11
24
|
|
|
12
25
|
/**
|
|
13
26
|
* Resolves and downloads all dependencies declared in the manifest, then writes
|
|
@@ -23,6 +36,32 @@ const { loadProfile, hashProfile, formatProfile, mergeProfile } = require('../pr
|
|
|
23
36
|
async function install(argv) {
|
|
24
37
|
const manifestPath = path.resolve(argv.manifest);
|
|
25
38
|
const rootDir = path.resolve(argv.root);
|
|
39
|
+
const buildMode = argv.build ?? 'never';
|
|
40
|
+
const uploadBuilt = argv.uploadBuilt === true;
|
|
41
|
+
const jsonOut = argv.format === 'json';
|
|
42
|
+
|
|
43
|
+
// Under --format=json, stdout is reserved for the final JSON payload.
|
|
44
|
+
// All info/success lines route to stderr via the logger.
|
|
45
|
+
if (jsonOut) log.setJsonMode(true);
|
|
46
|
+
|
|
47
|
+
// --upload-built implies a build step happened. `--build=never` +
|
|
48
|
+
// `--upload-built` is a usage error — fail fast rather than silently
|
|
49
|
+
// accept something that cannot upload anything.
|
|
50
|
+
if (uploadBuilt && buildMode === 'never') {
|
|
51
|
+
log.error(
|
|
52
|
+
'--upload-built requires --build=missing (or --build=always).\n' +
|
|
53
|
+
' Nothing will be built, so there is nothing to upload.',
|
|
54
|
+
);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (buildMode === 'always') {
|
|
59
|
+
log.error(
|
|
60
|
+
'--build=always is not implemented (requires PLAN-BUILD-MISSING phase 3).\n' +
|
|
61
|
+
' Use --build=never (default) or --build=missing.',
|
|
62
|
+
);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
26
65
|
|
|
27
66
|
// ── Auth / sources ────────────────────────────────────────────────────────
|
|
28
67
|
let packageSources;
|
|
@@ -32,13 +71,13 @@ async function install(argv) {
|
|
|
32
71
|
const config = readConfig();
|
|
33
72
|
packageSources = config.installSources.map((s) => s.url);
|
|
34
73
|
if (packageSources.length > 0) {
|
|
35
|
-
|
|
74
|
+
log.info(`Using ${packageSources.length} source(s) from config`);
|
|
36
75
|
}
|
|
37
76
|
}
|
|
38
77
|
|
|
39
78
|
if (packageSources.length === 0) {
|
|
40
|
-
|
|
41
|
-
'
|
|
79
|
+
log.warn(
|
|
80
|
+
'no sources configured. ' +
|
|
42
81
|
'Pass --source <url> or run: wyvrnpm configure add-source --kind install ...',
|
|
43
82
|
);
|
|
44
83
|
}
|
|
@@ -48,7 +87,7 @@ async function install(argv) {
|
|
|
48
87
|
const profileName = argv.profile ?? config.defaultProfile ?? 'default';
|
|
49
88
|
const { profile: baseProfile, fromFile } = loadProfile(profileName);
|
|
50
89
|
|
|
51
|
-
|
|
90
|
+
log.info(`Profile: "${profileName}"${fromFile ? '' : ' (auto-detected)'}`);
|
|
52
91
|
console.log(formatProfile(baseProfile).split('\n').map((l) => ` ${l}`).join('\n'));
|
|
53
92
|
console.log(` Profile hash : ${hashProfile(baseProfile)}`);
|
|
54
93
|
|
|
@@ -63,9 +102,9 @@ async function install(argv) {
|
|
|
63
102
|
const ver = typeof entry === 'string' ? entry : entry.version;
|
|
64
103
|
if (ver) lockedVersions.set(name, ver);
|
|
65
104
|
}
|
|
66
|
-
|
|
105
|
+
log.info(`Lock file found — ${lockedVersions.size} pinned package(s)`);
|
|
67
106
|
} catch {
|
|
68
|
-
|
|
107
|
+
log.warn('could not read wyvrn.lock, resolving from scratch');
|
|
69
108
|
}
|
|
70
109
|
}
|
|
71
110
|
|
|
@@ -80,46 +119,112 @@ async function install(argv) {
|
|
|
80
119
|
Object.entries(normalizedDeps).map(([name, d]) => [name, d.version]),
|
|
81
120
|
);
|
|
82
121
|
|
|
83
|
-
|
|
84
|
-
`
|
|
122
|
+
log.info(
|
|
123
|
+
`Installing dependencies for "${manifest.name}" (platform: ${argv.platform})`,
|
|
85
124
|
);
|
|
86
125
|
|
|
126
|
+
// Manifests collected during resolution — reused below to read each dep's
|
|
127
|
+
// recipe-declared `options` block without a second HTTP round trip.
|
|
128
|
+
const depManifests = new Map();
|
|
129
|
+
|
|
87
130
|
const resolvedDeps = await resolveDependencies(
|
|
88
131
|
depsForResolution,
|
|
89
132
|
packageSources,
|
|
90
133
|
argv.platform,
|
|
91
134
|
fetch,
|
|
92
135
|
lockedVersions,
|
|
136
|
+
depManifests,
|
|
93
137
|
);
|
|
94
138
|
|
|
139
|
+
// CLI `-o <pkg>:<name>=<value>` overrides, parsed into { pkg → {name: value} }.
|
|
140
|
+
const cliOptionsByPkg = parseCliOptions(argv.option);
|
|
141
|
+
|
|
95
142
|
// ── Build per-dependency enriched map ─────────────────────────────────────
|
|
96
|
-
// Each dep gets
|
|
97
|
-
//
|
|
143
|
+
// Each dep gets:
|
|
144
|
+
// - an effective profile (base + per-dep settings overrides)
|
|
145
|
+
// - effective options (recipe defaults + consumer overrides + CLI overrides)
|
|
146
|
+
// - profileHash computed from both (options only hashed in when declared)
|
|
98
147
|
const enrichedDeps = new Map();
|
|
99
148
|
for (const [name, version] of resolvedDeps) {
|
|
100
|
-
const depOverrides
|
|
149
|
+
const depOverrides = normalizedDeps[name]?.settings ?? {};
|
|
101
150
|
const effectiveProfile = mergeProfile(baseProfile, depOverrides);
|
|
102
|
-
|
|
151
|
+
|
|
152
|
+
const depManifest = depManifests.get(name);
|
|
153
|
+
const declaration = depManifest?.options
|
|
154
|
+
? normalizeOptionsDeclaration(depManifest.options, `${name}@${version}`)
|
|
155
|
+
: null;
|
|
156
|
+
const effectiveOptions = resolveEffectiveOptions(
|
|
157
|
+
declaration,
|
|
158
|
+
normalizedDeps[name]?.options ?? {},
|
|
159
|
+
cliOptionsByPkg[name] ?? {},
|
|
160
|
+
`${name}@${version}`,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const profileHash = hashProfile(effectiveProfile, effectiveOptions);
|
|
103
164
|
|
|
104
165
|
if (Object.keys(depOverrides).length > 0) {
|
|
105
|
-
|
|
106
|
-
|
|
166
|
+
log.info(
|
|
167
|
+
`${name}: settings override → profileHash ${profileHash}` +
|
|
107
168
|
` (${JSON.stringify(depOverrides)})`,
|
|
108
169
|
);
|
|
109
170
|
}
|
|
171
|
+
if (effectiveOptions) {
|
|
172
|
+
log.info(`${name}: options → ${JSON.stringify(effectiveOptions)}`);
|
|
173
|
+
}
|
|
110
174
|
|
|
111
|
-
enrichedDeps.set(name, {
|
|
175
|
+
enrichedDeps.set(name, {
|
|
176
|
+
version,
|
|
177
|
+
profileHash,
|
|
178
|
+
profile: effectiveProfile,
|
|
179
|
+
options: effectiveOptions,
|
|
180
|
+
});
|
|
112
181
|
}
|
|
113
182
|
|
|
114
183
|
const razerDir = path.join(rootDir, 'wyvrn_internal');
|
|
115
184
|
|
|
116
185
|
// Extract auth from the first install source (if any)
|
|
117
186
|
const firstInstallSrc = config.installSources?.[0];
|
|
118
|
-
const
|
|
187
|
+
const downloadOptions = {
|
|
119
188
|
awsProfile: firstInstallSrc?.profile ?? undefined,
|
|
120
189
|
token: firstInstallSrc?.token ?? undefined,
|
|
190
|
+
buildMode,
|
|
191
|
+
profileName,
|
|
121
192
|
};
|
|
122
193
|
|
|
194
|
+
// ── Resolve upload destination up-front when --upload-built is set ──────
|
|
195
|
+
// Failing here (no publish source configured) avoids spending minutes on
|
|
196
|
+
// a source-build whose output we can't deliver.
|
|
197
|
+
if (uploadBuilt) {
|
|
198
|
+
const resolved = resolveUploadDestination(
|
|
199
|
+
{
|
|
200
|
+
uploadSource: argv.uploadSource,
|
|
201
|
+
awsProfile: argv.awsProfile ?? firstInstallSrc?.profile,
|
|
202
|
+
token: argv.token ?? firstInstallSrc?.token,
|
|
203
|
+
},
|
|
204
|
+
config,
|
|
205
|
+
);
|
|
206
|
+
if (!resolved) {
|
|
207
|
+
log.error(
|
|
208
|
+
'--upload-built was passed but no upload destination could be resolved.\n' +
|
|
209
|
+
' Pass --upload-source <url-or-name> or configure a publish source:\n' +
|
|
210
|
+
' wyvrnpm configure add-source --kind publish --name ... --url ...',
|
|
211
|
+
);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
downloadOptions.uploadBuilt = true;
|
|
215
|
+
downloadOptions.uploadSource = resolved.uploadSource;
|
|
216
|
+
downloadOptions.uploadAuth = resolved.uploadAuth;
|
|
217
|
+
downloadOptions.uploadStats = createUploadStats();
|
|
218
|
+
|
|
219
|
+
if (resolved.uploadSource !== (packageSources[0] ?? '')) {
|
|
220
|
+
log.info(
|
|
221
|
+
`upload-built: install source(s) and upload source differ —\n` +
|
|
222
|
+
` install: ${packageSources.join(', ') || '(none)'}\n` +
|
|
223
|
+
` upload : ${resolved.uploadSource}`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
123
228
|
const lockEntries = await downloadDependencies(
|
|
124
229
|
enrichedDeps,
|
|
125
230
|
packageSources,
|
|
@@ -127,7 +232,7 @@ async function install(argv) {
|
|
|
127
232
|
razerDir,
|
|
128
233
|
fetch,
|
|
129
234
|
argv.timeout,
|
|
130
|
-
|
|
235
|
+
downloadOptions,
|
|
131
236
|
);
|
|
132
237
|
|
|
133
238
|
// ── Write v2 lock file ────────────────────────────────────────────────────
|
|
@@ -147,10 +252,148 @@ async function install(argv) {
|
|
|
147
252
|
};
|
|
148
253
|
|
|
149
254
|
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n', 'utf8');
|
|
150
|
-
|
|
255
|
+
log.info(`Lock file written to ${lockPath}`);
|
|
256
|
+
|
|
257
|
+
// ── CMake toolchain generation ────────────────────────────────────────────
|
|
258
|
+
const packageNames = [...resolvedDeps.keys()].sort();
|
|
259
|
+
const { toolchain } = generateToolchain({
|
|
260
|
+
destDir: razerDir,
|
|
261
|
+
profile: baseProfile,
|
|
262
|
+
profileName,
|
|
263
|
+
profileHash: hashProfile(baseProfile),
|
|
264
|
+
packageNames,
|
|
265
|
+
});
|
|
266
|
+
log.info(`CMake toolchain written to ${toolchain}`);
|
|
267
|
+
|
|
268
|
+
// ── CMakePresets.json emission ────────────────────────────────────────────
|
|
269
|
+
const toolchainRelPath = path
|
|
270
|
+
.relative(rootDir, toolchain)
|
|
271
|
+
.replace(/\\/g, '/');
|
|
272
|
+
|
|
273
|
+
// Derive generator + cacheVariables from the project's own `build` recipe
|
|
274
|
+
// so `wyvrnpm build` locally honours the same knobs the source-build /
|
|
275
|
+
// publish paths use. Without this, `build.configure` is advisory metadata
|
|
276
|
+
// and CMake falls back to its default generator (VS on Windows) — which
|
|
277
|
+
// breaks publishers whose recipe disables example/test subprojects (F15).
|
|
278
|
+
let presetGenerator = null;
|
|
279
|
+
let presetCacheVariables = null;
|
|
280
|
+
let presetInstallDir = null;
|
|
281
|
+
if (hasBuildRecipe(manifest)) {
|
|
282
|
+
try {
|
|
283
|
+
// Resolve the project's own options to expand ${options.*} in its
|
|
284
|
+
// configure args. When the project declares no options, this is null
|
|
285
|
+
// and the recipe's configure / buildArgs must not contain templates
|
|
286
|
+
// (normalizeRecipe enforces that).
|
|
287
|
+
const projectDeclaration = manifest.options
|
|
288
|
+
? normalizeOptionsDeclaration(manifest.options, manifest.name)
|
|
289
|
+
: null;
|
|
290
|
+
const projectCliOverrides = cliOptionsByPkg[manifest.name] ?? {};
|
|
291
|
+
const projectEffectiveOptions = resolveEffectiveOptions(
|
|
292
|
+
projectDeclaration, {}, projectCliOverrides, manifest.name,
|
|
293
|
+
);
|
|
294
|
+
const recipe = normalizeRecipe(manifest.build, projectEffectiveOptions);
|
|
295
|
+
|
|
296
|
+
if (recipe.generators.length > 0) {
|
|
297
|
+
// First entry in the generator fallback chain. Users whose system
|
|
298
|
+
// lacks the first pick can override via CMakeUserPresets.json
|
|
299
|
+
// (which CMake merges in automatically).
|
|
300
|
+
[presetGenerator] = recipe.generators;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Fold -DKEY=VALUE from recipe.configure into cacheVariables. Other
|
|
304
|
+
// configure args (anything not matching `-DKEY=VALUE`) can't be
|
|
305
|
+
// expressed in a CMake preset and are silently skipped here — the
|
|
306
|
+
// source-build path still sees them verbatim.
|
|
307
|
+
const cacheVars = {};
|
|
308
|
+
for (const arg of recipe.configure) {
|
|
309
|
+
const match = arg.match(/^-D([^=]+)=(.*)$/);
|
|
310
|
+
if (match) cacheVars[match[1]] = match[2];
|
|
311
|
+
}
|
|
312
|
+
if (Object.keys(cacheVars).length > 0) presetCacheVariables = cacheVars;
|
|
313
|
+
|
|
314
|
+
// Propagate `build.installDir` so that `wyvrnpm build --install` lands
|
|
315
|
+
// under the project's build tree instead of CMake's system default
|
|
316
|
+
// (C:/Program Files on Windows, /usr/local on Unix). Same semantics as
|
|
317
|
+
// the source-build path which passes -DCMAKE_INSTALL_PREFIX explicitly.
|
|
318
|
+
presetInstallDir = recipe.installDir;
|
|
319
|
+
} catch (err) {
|
|
320
|
+
log.warn(
|
|
321
|
+
`could not derive CMake preset config from the project's build recipe: ${err.message}\n` +
|
|
322
|
+
` the preset will be emitted without a generator or recipe cacheVariables. ` +
|
|
323
|
+
`Fix the recipe or override via CMakeUserPresets.json.`,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const presetResult = generateCMakePresets({
|
|
329
|
+
projectRoot: rootDir,
|
|
330
|
+
profileName,
|
|
331
|
+
profileHash: hashProfile(baseProfile),
|
|
332
|
+
toolchainRelPath,
|
|
333
|
+
generator: presetGenerator,
|
|
334
|
+
extraCacheVariables: presetCacheVariables,
|
|
335
|
+
installDir: presetInstallDir,
|
|
336
|
+
});
|
|
337
|
+
if (presetResult.action === 'skipped') {
|
|
338
|
+
log.warn(
|
|
339
|
+
'could not write presets — both CMakePresets.json and ' +
|
|
340
|
+
'CMakeUserPresets.json exist and are user-owned. Add this to your presets:\n' +
|
|
341
|
+
` { "name": "wyvrn-${profileName}", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "\${sourceDir}/${toolchainRelPath}" } }`,
|
|
342
|
+
);
|
|
343
|
+
} else {
|
|
344
|
+
log.info(
|
|
345
|
+
`CMake presets ${presetResult.action}: ${presetResult.path}` +
|
|
346
|
+
(presetResult.isUser ? ' (user presets — CMakePresets.json is user-owned)' : ''),
|
|
347
|
+
);
|
|
348
|
+
}
|
|
151
349
|
|
|
152
350
|
const count = resolvedDeps.size;
|
|
153
|
-
|
|
351
|
+
log.success(`Done — ${count} package${count !== 1 ? 's' : ''} installed.`);
|
|
352
|
+
|
|
353
|
+
// ── upload-built summary ────────────────────────────────────────────────
|
|
354
|
+
const uploadSummary = formatUploadSummary(downloadOptions.uploadStats);
|
|
355
|
+
if (uploadSummary) log.info(uploadSummary);
|
|
356
|
+
|
|
357
|
+
// ── JSON payload (--format=json) ────────────────────────────────────────
|
|
358
|
+
// All log.info/success above have gone to stderr thanks to setJsonMode.
|
|
359
|
+
// stdout gets exactly one JSON object, no trailing text.
|
|
360
|
+
if (jsonOut) {
|
|
361
|
+
const packages = [...resolvedDeps.keys()].sort().map((name) => {
|
|
362
|
+
const entry = lockEntries?.get(name) ?? {};
|
|
363
|
+
const consumerSpec = normalizedDeps[name]?.version ?? null;
|
|
364
|
+
const resolvedVer = entry.version ?? resolvedDeps.get(name);
|
|
365
|
+
const versionRange = (consumerSpec && consumerSpec !== resolvedVer) ? consumerSpec : null;
|
|
366
|
+
return {
|
|
367
|
+
name,
|
|
368
|
+
version: resolvedVer,
|
|
369
|
+
versionRange,
|
|
370
|
+
profileHash: entry.profileHash ?? null,
|
|
371
|
+
options: entry.options ?? null,
|
|
372
|
+
contentSha256: entry.contentSha256 ?? null,
|
|
373
|
+
gitSha: entry.gitSha ?? null,
|
|
374
|
+
resolvedFrom: entry.resolvedFrom ?? null,
|
|
375
|
+
};
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const stats = downloadOptions.uploadStats;
|
|
379
|
+
const uploadSummaryJson = stats ? {
|
|
380
|
+
uploaded: stats.uploaded.length,
|
|
381
|
+
skipped: stats.skipped.length,
|
|
382
|
+
failed: stats.failed.length,
|
|
383
|
+
} : null;
|
|
384
|
+
|
|
385
|
+
const payload = {
|
|
386
|
+
command: 'install',
|
|
387
|
+
wyvrnpmVersion: require('../../package.json').version,
|
|
388
|
+
profile: baseProfile,
|
|
389
|
+
profileName,
|
|
390
|
+
profileHash: hashProfile(baseProfile),
|
|
391
|
+
packages,
|
|
392
|
+
lockFile: lockPath,
|
|
393
|
+
uploadSummary: uploadSummaryJson,
|
|
394
|
+
};
|
|
395
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
396
|
+
}
|
|
154
397
|
}
|
|
155
398
|
|
|
156
399
|
module.exports = install;
|
package/src/commands/link.js
CHANGED
|
@@ -7,6 +7,7 @@ const { readManifest } = require('../manifest');
|
|
|
7
7
|
const { createLink, isLink, removeLink, getLinkTarget } = require('../link-utils');
|
|
8
8
|
const { downloadDependencies } = require('../download');
|
|
9
9
|
const { resolveDependencies } = require('../resolve');
|
|
10
|
+
const log = require('../logger');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Normalizes a linked package entry to object form.
|
|
@@ -47,7 +48,7 @@ function registerGlobal(manifestPath, subdir) {
|
|
|
47
48
|
const packageName = manifest.name || manifest.Name;
|
|
48
49
|
|
|
49
50
|
if (!packageName) {
|
|
50
|
-
|
|
51
|
+
log.error('wyvrn.json must have a "name" field');
|
|
51
52
|
process.exit(1);
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -63,7 +64,7 @@ function registerGlobal(manifestPath, subdir) {
|
|
|
63
64
|
writeConfig(config);
|
|
64
65
|
|
|
65
66
|
const effectivePath = getEffectivePath(entry);
|
|
66
|
-
|
|
67
|
+
log.info(`Registered "${packageName}" → ${effectivePath}`);
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
/**
|
|
@@ -77,7 +78,7 @@ function linkToProject(name, sourcePath, rootDir) {
|
|
|
77
78
|
const absoluteSource = path.resolve(sourcePath);
|
|
78
79
|
|
|
79
80
|
if (!fs.existsSync(absoluteSource)) {
|
|
80
|
-
|
|
81
|
+
log.error(`source path does not exist: ${absoluteSource}`);
|
|
81
82
|
process.exit(1);
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -106,7 +107,7 @@ function linkToProject(name, sourcePath, rootDir) {
|
|
|
106
107
|
lockData.generatedAt = new Date().toISOString();
|
|
107
108
|
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n', 'utf8');
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
log.info(`Linked: ${name} → ${absoluteSource}`);
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
/**
|
|
@@ -122,18 +123,18 @@ async function unlinkFromProject(name, rootDir, restore, argv) {
|
|
|
122
123
|
const linkPath = path.join(razerDir, name);
|
|
123
124
|
|
|
124
125
|
if (!fs.existsSync(linkPath)) {
|
|
125
|
-
|
|
126
|
+
log.error(`package "${name}" is not installed`);
|
|
126
127
|
process.exit(1);
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
if (!isLink(linkPath)) {
|
|
130
|
-
|
|
131
|
+
log.error(`package "${name}" is not a linked package`);
|
|
131
132
|
process.exit(1);
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
// Remove the symlink
|
|
135
136
|
removeLink(linkPath);
|
|
136
|
-
|
|
137
|
+
log.info(`Unlinked: ${name}`);
|
|
137
138
|
|
|
138
139
|
// Update the lock file
|
|
139
140
|
const lockPath = path.join(rootDir, 'wyvrn.lock');
|
|
@@ -161,7 +162,7 @@ async function unlinkFromProject(name, rootDir, restore, argv) {
|
|
|
161
162
|
: rawDeps;
|
|
162
163
|
|
|
163
164
|
if (!deps[name]) {
|
|
164
|
-
|
|
165
|
+
log.warn(`"${name}" is not in manifest dependencies, cannot restore`);
|
|
165
166
|
return;
|
|
166
167
|
}
|
|
167
168
|
|
|
@@ -175,7 +176,7 @@ async function unlinkFromProject(name, rootDir, restore, argv) {
|
|
|
175
176
|
}
|
|
176
177
|
|
|
177
178
|
if (packageSources.length === 0) {
|
|
178
|
-
|
|
179
|
+
log.warn('no sources configured, cannot restore package');
|
|
179
180
|
return;
|
|
180
181
|
}
|
|
181
182
|
|
|
@@ -223,11 +224,11 @@ function listLinked() {
|
|
|
223
224
|
const entries = Object.entries(linked);
|
|
224
225
|
|
|
225
226
|
if (entries.length === 0) {
|
|
226
|
-
|
|
227
|
+
log.info('No globally registered packages');
|
|
227
228
|
return;
|
|
228
229
|
}
|
|
229
230
|
|
|
230
|
-
|
|
231
|
+
log.info('Globally registered packages:');
|
|
231
232
|
for (const [name, rawEntry] of entries) {
|
|
232
233
|
const entry = normalizeLinkedEntry(rawEntry);
|
|
233
234
|
const effectivePath = getEffectivePath(entry);
|
|
@@ -256,7 +257,7 @@ async function link(argv) {
|
|
|
256
257
|
// wyvrnpm link (no args) - register current package globally
|
|
257
258
|
if (!argv.name) {
|
|
258
259
|
if (!fs.existsSync(manifestPath)) {
|
|
259
|
-
|
|
260
|
+
log.error(`no wyvrn.json found at ${manifestPath}`);
|
|
260
261
|
process.exit(1);
|
|
261
262
|
}
|
|
262
263
|
registerGlobal(manifestPath, argv.subdir);
|
|
@@ -278,9 +279,11 @@ async function link(argv) {
|
|
|
278
279
|
const linked = config.linkedPackages || {};
|
|
279
280
|
|
|
280
281
|
if (!linked[argv.name]) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
282
|
+
log.error(
|
|
283
|
+
`"${argv.name}" is not registered globally\n` +
|
|
284
|
+
`Run "wyvrnpm link" in the package directory first, or use:\n` +
|
|
285
|
+
` wyvrnpm link ${argv.name} <path>`,
|
|
286
|
+
);
|
|
284
287
|
process.exit(1);
|
|
285
288
|
}
|
|
286
289
|
|
package/src/commands/profile.js
CHANGED
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
mergeProfile,
|
|
13
13
|
} = require('../profile');
|
|
14
14
|
const { readConfig, writeConfig } = require('../config');
|
|
15
|
+
const log = require('../logger');
|
|
15
16
|
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
// list
|
|
@@ -27,8 +28,9 @@ function list() {
|
|
|
27
28
|
const dir = getProfilesDir();
|
|
28
29
|
const defName = config.defaultProfile ?? 'default';
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
log.info(`Profiles directory: ${dir}`);
|
|
32
|
+
log.info(`Default profile : ${defName}`);
|
|
33
|
+
console.log('');
|
|
32
34
|
|
|
33
35
|
if (names.length === 0) {
|
|
34
36
|
console.log(' (no profiles saved — run `wyvrnpm configure profile detect` to create one)');
|
|
@@ -60,12 +62,12 @@ function show(argv) {
|
|
|
60
62
|
const stored = readProfile(name);
|
|
61
63
|
|
|
62
64
|
if (stored) {
|
|
63
|
-
|
|
65
|
+
log.info(`Profile "${name}":`);
|
|
64
66
|
console.log(formatProfile(stored));
|
|
65
67
|
console.log(`\nProfile hash : ${hashProfile(stored)}`);
|
|
66
68
|
} else {
|
|
67
69
|
const detected = detectProfile();
|
|
68
|
-
|
|
70
|
+
log.info(`Profile "${name}" not found — showing auto-detected environment:`);
|
|
69
71
|
console.log(formatProfile(detected));
|
|
70
72
|
console.log(`\nProfile hash : ${hashProfile(detected)}`);
|
|
71
73
|
console.log(`\nRun \`wyvrnpm configure profile detect --name ${name}\` to save this.`);
|
|
@@ -85,13 +87,14 @@ function detect(argv) {
|
|
|
85
87
|
const name = argv.name ?? config.defaultProfile ?? 'default';
|
|
86
88
|
const p = detectProfile();
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
log.info('Detected profile:');
|
|
89
91
|
console.log(formatProfile(p));
|
|
90
92
|
console.log(`\nProfile hash : ${hashProfile(p)}`);
|
|
91
93
|
|
|
92
94
|
if (!argv.dryRun) {
|
|
93
95
|
writeProfile(name, p);
|
|
94
|
-
console.log(
|
|
96
|
+
console.log('');
|
|
97
|
+
log.success(`Saved as profile "${name}".`);
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
100
|
|
|
@@ -120,7 +123,7 @@ function set(argv) {
|
|
|
120
123
|
|
|
121
124
|
writeProfile(name, updated);
|
|
122
125
|
|
|
123
|
-
|
|
126
|
+
log.info(`Profile "${name}" updated:`);
|
|
124
127
|
console.log(formatProfile(updated));
|
|
125
128
|
console.log(`\nProfile hash : ${hashProfile(updated)}`);
|
|
126
129
|
}
|
|
@@ -136,13 +139,13 @@ function set(argv) {
|
|
|
136
139
|
function setDefault(argv) {
|
|
137
140
|
const name = argv.name;
|
|
138
141
|
if (!readProfile(name)) {
|
|
139
|
-
|
|
142
|
+
log.error(`profile "${name}" does not exist. Create it first.`);
|
|
140
143
|
process.exit(1);
|
|
141
144
|
}
|
|
142
145
|
const config = readConfig();
|
|
143
146
|
config.defaultProfile = name;
|
|
144
147
|
writeConfig(config);
|
|
145
|
-
|
|
148
|
+
log.info(`Default profile set to "${name}".`);
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
// ---------------------------------------------------------------------------
|
|
@@ -156,14 +159,14 @@ function setDefault(argv) {
|
|
|
156
159
|
function del(argv) {
|
|
157
160
|
const name = argv.name;
|
|
158
161
|
if (name === 'default') {
|
|
159
|
-
|
|
162
|
+
log.error('cannot delete the "default" profile.');
|
|
160
163
|
process.exit(1);
|
|
161
164
|
}
|
|
162
165
|
const removed = deleteProfile(name);
|
|
163
166
|
if (removed) {
|
|
164
|
-
|
|
167
|
+
log.info(`Profile "${name}" deleted.`);
|
|
165
168
|
} else {
|
|
166
|
-
|
|
169
|
+
log.error(`profile "${name}" not found.`);
|
|
167
170
|
process.exit(1);
|
|
168
171
|
}
|
|
169
172
|
}
|