wyvrnpm 1.2.1 → 2.0.1
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 +513 -413
- package/bin/wyvrn.js +277 -163
- package/package.json +37 -37
- package/src/commands/install.js +156 -109
- package/src/commands/link.js +316 -0
- package/src/commands/profile.js +171 -0
- package/src/commands/publish.js +232 -180
- package/src/config.js +63 -60
- package/src/download.js +325 -164
- package/src/index.js +10 -9
- package/src/link-utils.js +95 -0
- package/src/manifest.js +133 -77
- package/src/profile.js +377 -0
- package/src/providers/base.js +133 -56
- package/src/providers/file.js +181 -71
- package/src/providers/http.js +268 -118
- package/src/providers/s3.js +273 -136
- package/src/resolve.js +262 -200
package/src/commands/install.js
CHANGED
|
@@ -1,109 +1,156 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
const {
|
|
8
|
-
const {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
argv.platform
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const { readManifest, normalizeDependencies } = require('../manifest');
|
|
7
|
+
const { resolveDependencies } = require('../resolve');
|
|
8
|
+
const { downloadDependencies } = require('../download');
|
|
9
|
+
const { readConfig } = require('../config');
|
|
10
|
+
const { loadProfile, hashProfile, formatProfile, mergeProfile } = require('../profile');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolves and downloads all dependencies declared in the manifest, then writes
|
|
14
|
+
* a wyvrn.lock file recording exactly what was installed.
|
|
15
|
+
*
|
|
16
|
+
* v2 behaviour:
|
|
17
|
+
* - Loads the named build profile (--profile flag → config.defaultProfile → "default")
|
|
18
|
+
* - Each dependency can override individual profile settings via its `settings` field
|
|
19
|
+
* - Lock file records the full profile + per-package SHA256, profileHash, and git metadata
|
|
20
|
+
*
|
|
21
|
+
* @param {object} argv
|
|
22
|
+
*/
|
|
23
|
+
async function install(argv) {
|
|
24
|
+
const manifestPath = path.resolve(argv.manifest);
|
|
25
|
+
const rootDir = path.resolve(argv.root);
|
|
26
|
+
|
|
27
|
+
// ── Auth / sources ────────────────────────────────────────────────────────
|
|
28
|
+
let packageSources;
|
|
29
|
+
if (argv.source && argv.source.length > 0) {
|
|
30
|
+
packageSources = argv.source;
|
|
31
|
+
} else {
|
|
32
|
+
const config = readConfig();
|
|
33
|
+
packageSources = config.installSources.map((s) => s.url);
|
|
34
|
+
if (packageSources.length > 0) {
|
|
35
|
+
console.log(`[wyvrn] Using ${packageSources.length} source(s) from config`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (packageSources.length === 0) {
|
|
40
|
+
console.warn(
|
|
41
|
+
'[wyvrn] Warning: no sources configured. ' +
|
|
42
|
+
'Pass --source <url> or run: wyvrnpm configure add-source --kind install ...',
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── Active build profile ──────────────────────────────────────────────────
|
|
47
|
+
const config = readConfig();
|
|
48
|
+
const profileName = argv.profile ?? config.defaultProfile ?? 'default';
|
|
49
|
+
const { profile: baseProfile, fromFile } = loadProfile(profileName);
|
|
50
|
+
|
|
51
|
+
console.log(`[wyvrn] Profile: "${profileName}"${fromFile ? '' : ' (auto-detected)'}`);
|
|
52
|
+
console.log(formatProfile(baseProfile).split('\n').map((l) => ` ${l}`).join('\n'));
|
|
53
|
+
console.log(` Profile hash : ${hashProfile(baseProfile)}`);
|
|
54
|
+
|
|
55
|
+
// ── Lock file ─────────────────────────────────────────────────────────────
|
|
56
|
+
const lockPath = path.join(path.dirname(manifestPath), 'wyvrn.lock');
|
|
57
|
+
const lockedVersions = new Map();
|
|
58
|
+
|
|
59
|
+
if (fs.existsSync(lockPath)) {
|
|
60
|
+
try {
|
|
61
|
+
const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
|
62
|
+
for (const [name, entry] of Object.entries(lock.packages ?? {})) {
|
|
63
|
+
const ver = typeof entry === 'string' ? entry : entry.version;
|
|
64
|
+
if (ver) lockedVersions.set(name, ver);
|
|
65
|
+
}
|
|
66
|
+
console.log(`[wyvrn] Lock file found — ${lockedVersions.size} pinned package(s)`);
|
|
67
|
+
} catch {
|
|
68
|
+
console.warn('[wyvrn] Warning: could not read wyvrn.lock, resolving from scratch');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── Read manifest ─────────────────────────────────────────────────────────
|
|
73
|
+
const manifest = readManifest(manifestPath);
|
|
74
|
+
|
|
75
|
+
// Normalize to { name: { version, settings } }
|
|
76
|
+
const normalizedDeps = normalizeDependencies(manifest.dependencies ?? manifest.Dependencies);
|
|
77
|
+
|
|
78
|
+
// For resolution, only pass name → version (settings don't affect resolution)
|
|
79
|
+
const depsForResolution = Object.fromEntries(
|
|
80
|
+
Object.entries(normalizedDeps).map(([name, d]) => [name, d.version]),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
console.log(
|
|
84
|
+
`[wyvrn] Installing dependencies for "${manifest.name}" (platform: ${argv.platform})`,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const resolvedDeps = await resolveDependencies(
|
|
88
|
+
depsForResolution,
|
|
89
|
+
packageSources,
|
|
90
|
+
argv.platform,
|
|
91
|
+
fetch,
|
|
92
|
+
lockedVersions,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// ── Build per-dependency enriched map ─────────────────────────────────────
|
|
96
|
+
// Each dep gets a profileHash based on base profile + any settings overrides
|
|
97
|
+
// declared in wyvrn.json under that dependency's "settings" key.
|
|
98
|
+
const enrichedDeps = new Map();
|
|
99
|
+
for (const [name, version] of resolvedDeps) {
|
|
100
|
+
const depOverrides = normalizedDeps[name]?.settings ?? {};
|
|
101
|
+
const effectiveProfile = mergeProfile(baseProfile, depOverrides);
|
|
102
|
+
const profileHash = hashProfile(effectiveProfile);
|
|
103
|
+
|
|
104
|
+
if (Object.keys(depOverrides).length > 0) {
|
|
105
|
+
console.log(
|
|
106
|
+
`[wyvrn] ${name}: settings override → profileHash ${profileHash}` +
|
|
107
|
+
` (${JSON.stringify(depOverrides)})`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
enrichedDeps.set(name, { version, profileHash, profile: effectiveProfile });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const razerDir = path.join(rootDir, 'wyvrn_internal');
|
|
115
|
+
|
|
116
|
+
// Extract auth from the first install source (if any)
|
|
117
|
+
const firstInstallSrc = config.installSources?.[0];
|
|
118
|
+
const authOptions = {
|
|
119
|
+
awsProfile: firstInstallSrc?.profile ?? undefined,
|
|
120
|
+
token: firstInstallSrc?.token ?? undefined,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const lockEntries = await downloadDependencies(
|
|
124
|
+
enrichedDeps,
|
|
125
|
+
packageSources,
|
|
126
|
+
argv.platform,
|
|
127
|
+
razerDir,
|
|
128
|
+
fetch,
|
|
129
|
+
argv.timeout,
|
|
130
|
+
authOptions,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// ── Write v2 lock file ────────────────────────────────────────────────────
|
|
134
|
+
const sortedPackages = {};
|
|
135
|
+
for (const key of [...resolvedDeps.keys()].sort()) {
|
|
136
|
+
sortedPackages[key] = lockEntries?.get(key) ?? { version: resolvedDeps.get(key) };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const lockData = {
|
|
140
|
+
wyvrnVersion: 2,
|
|
141
|
+
generatedAt: new Date().toISOString(),
|
|
142
|
+
platform: argv.platform,
|
|
143
|
+
profileName,
|
|
144
|
+
profile: baseProfile,
|
|
145
|
+
profileHash: hashProfile(baseProfile),
|
|
146
|
+
packages: sortedPackages,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n', 'utf8');
|
|
150
|
+
console.log(`[wyvrn] Lock file written to ${lockPath}`);
|
|
151
|
+
|
|
152
|
+
const count = resolvedDeps.size;
|
|
153
|
+
console.log(`[wyvrn] Done — ${count} package${count !== 1 ? 's' : ''} installed.`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = install;
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { readConfig, writeConfig } = require('../config');
|
|
6
|
+
const { readManifest } = require('../manifest');
|
|
7
|
+
const { createLink, isLink, removeLink, getLinkTarget } = require('../link-utils');
|
|
8
|
+
const { downloadDependencies } = require('../download');
|
|
9
|
+
const { resolveDependencies } = require('../resolve');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Normalizes a linked package entry to object form.
|
|
13
|
+
* Supports both legacy string format and new object format.
|
|
14
|
+
*
|
|
15
|
+
* @param {string|object} entry - Either a path string or {path, subdir} object.
|
|
16
|
+
* @returns {{path: string, subdir?: string}}
|
|
17
|
+
*/
|
|
18
|
+
function normalizeLinkedEntry(entry) {
|
|
19
|
+
if (typeof entry === 'string') {
|
|
20
|
+
return { path: entry };
|
|
21
|
+
}
|
|
22
|
+
return entry;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Gets the effective link target path (combining path + subdir).
|
|
27
|
+
*
|
|
28
|
+
* @param {{path: string, subdir?: string}} entry - Linked package entry.
|
|
29
|
+
* @returns {string} The full path to link to.
|
|
30
|
+
*/
|
|
31
|
+
function getEffectivePath(entry) {
|
|
32
|
+
if (entry.subdir) {
|
|
33
|
+
return path.join(entry.path, entry.subdir);
|
|
34
|
+
}
|
|
35
|
+
return entry.path;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Register the current directory's package globally for linking.
|
|
40
|
+
* Reads wyvrn.json to get the package name, stores {name: {path, subdir}} in config.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} manifestPath - Path to wyvrn.json
|
|
43
|
+
* @param {string} [subdir] - Optional subdirectory within the package to link.
|
|
44
|
+
*/
|
|
45
|
+
function registerGlobal(manifestPath, subdir) {
|
|
46
|
+
const manifest = readManifest(manifestPath);
|
|
47
|
+
const packageName = manifest.name || manifest.Name;
|
|
48
|
+
|
|
49
|
+
if (!packageName) {
|
|
50
|
+
console.error('[wyvrn] Error: wyvrn.json must have a "name" field');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const packagePath = path.dirname(path.resolve(manifestPath));
|
|
55
|
+
const config = readConfig();
|
|
56
|
+
config.linkedPackages = config.linkedPackages || {};
|
|
57
|
+
|
|
58
|
+
const entry = { path: packagePath };
|
|
59
|
+
if (subdir) {
|
|
60
|
+
entry.subdir = subdir;
|
|
61
|
+
}
|
|
62
|
+
config.linkedPackages[packageName] = entry;
|
|
63
|
+
writeConfig(config);
|
|
64
|
+
|
|
65
|
+
const effectivePath = getEffectivePath(entry);
|
|
66
|
+
console.log(`[wyvrn] Registered "${packageName}" → ${effectivePath}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Link a package into the current project's wyvrn_internal directory.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} name - Package name to link.
|
|
73
|
+
* @param {string} sourcePath - Source directory path (absolute or relative).
|
|
74
|
+
* @param {string} rootDir - Project root directory.
|
|
75
|
+
*/
|
|
76
|
+
function linkToProject(name, sourcePath, rootDir) {
|
|
77
|
+
const absoluteSource = path.resolve(sourcePath);
|
|
78
|
+
|
|
79
|
+
if (!fs.existsSync(absoluteSource)) {
|
|
80
|
+
console.error(`[wyvrn] Error: source path does not exist: ${absoluteSource}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const razerDir = path.join(rootDir, 'wyvrn_internal');
|
|
85
|
+
const linkPath = path.join(razerDir, name);
|
|
86
|
+
|
|
87
|
+
// Create the link
|
|
88
|
+
createLink(absoluteSource, linkPath);
|
|
89
|
+
|
|
90
|
+
// Write a .wyvrn-version file to indicate it's linked
|
|
91
|
+
const versionFile = path.join(linkPath, '.wyvrn-version');
|
|
92
|
+
fs.writeFileSync(versionFile, 'linked', 'utf8');
|
|
93
|
+
|
|
94
|
+
// Update the lock file to record the link
|
|
95
|
+
const lockPath = path.join(rootDir, 'wyvrn.lock');
|
|
96
|
+
let lockData = { packages: {} };
|
|
97
|
+
if (fs.existsSync(lockPath)) {
|
|
98
|
+
try {
|
|
99
|
+
lockData = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
|
100
|
+
} catch {
|
|
101
|
+
// Use default if parsing fails
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
lockData.packages = lockData.packages || {};
|
|
105
|
+
lockData.packages[name] = `linked:${absoluteSource}`;
|
|
106
|
+
lockData.generatedAt = new Date().toISOString();
|
|
107
|
+
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n', 'utf8');
|
|
108
|
+
|
|
109
|
+
console.log(`[wyvrn] Linked: ${name} → ${absoluteSource}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Unlink a package from the current project.
|
|
114
|
+
*
|
|
115
|
+
* @param {string} name - Package name to unlink.
|
|
116
|
+
* @param {string} rootDir - Project root directory.
|
|
117
|
+
* @param {boolean} restore - Whether to re-download from registry.
|
|
118
|
+
* @param {object} argv - Original argv for re-download options.
|
|
119
|
+
*/
|
|
120
|
+
async function unlinkFromProject(name, rootDir, restore, argv) {
|
|
121
|
+
const razerDir = path.join(rootDir, 'wyvrn_internal');
|
|
122
|
+
const linkPath = path.join(razerDir, name);
|
|
123
|
+
|
|
124
|
+
if (!fs.existsSync(linkPath)) {
|
|
125
|
+
console.error(`[wyvrn] Error: package "${name}" is not installed`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!isLink(linkPath)) {
|
|
130
|
+
console.error(`[wyvrn] Error: package "${name}" is not a linked package`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Remove the symlink
|
|
135
|
+
removeLink(linkPath);
|
|
136
|
+
console.log(`[wyvrn] Unlinked: ${name}`);
|
|
137
|
+
|
|
138
|
+
// Update the lock file
|
|
139
|
+
const lockPath = path.join(rootDir, 'wyvrn.lock');
|
|
140
|
+
if (fs.existsSync(lockPath)) {
|
|
141
|
+
try {
|
|
142
|
+
const lockData = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
|
143
|
+
if (lockData.packages && lockData.packages[name]) {
|
|
144
|
+
delete lockData.packages[name];
|
|
145
|
+
lockData.generatedAt = new Date().toISOString();
|
|
146
|
+
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n', 'utf8');
|
|
147
|
+
}
|
|
148
|
+
} catch {
|
|
149
|
+
// Ignore lock file errors
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Re-download from registry if requested
|
|
154
|
+
if (restore) {
|
|
155
|
+
const manifestPath = path.resolve(argv.manifest);
|
|
156
|
+
const manifest = readManifest(manifestPath);
|
|
157
|
+
|
|
158
|
+
const rawDeps = manifest.dependencies || manifest.Dependencies || {};
|
|
159
|
+
const deps = Array.isArray(rawDeps)
|
|
160
|
+
? Object.fromEntries(rawDeps.map((d) => [d.Name ?? d.name, d.Version ?? d.version]))
|
|
161
|
+
: rawDeps;
|
|
162
|
+
|
|
163
|
+
if (!deps[name]) {
|
|
164
|
+
console.warn(`[wyvrn] Warning: "${name}" is not in manifest dependencies, cannot restore`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Get package sources
|
|
169
|
+
let packageSources;
|
|
170
|
+
if (argv.source && argv.source.length > 0) {
|
|
171
|
+
packageSources = argv.source;
|
|
172
|
+
} else {
|
|
173
|
+
const config = readConfig();
|
|
174
|
+
packageSources = config.installSources.map((s) => s.url);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (packageSources.length === 0) {
|
|
178
|
+
console.warn('[wyvrn] Warning: no sources configured, cannot restore package');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Resolve just this one dependency
|
|
183
|
+
const resolvedDeps = await resolveDependencies(
|
|
184
|
+
{ [name]: deps[name] },
|
|
185
|
+
packageSources,
|
|
186
|
+
argv.platform || 'win_x64',
|
|
187
|
+
fetch,
|
|
188
|
+
new Map(),
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
// Download it
|
|
192
|
+
await downloadDependencies(
|
|
193
|
+
resolvedDeps,
|
|
194
|
+
packageSources,
|
|
195
|
+
argv.platform || 'win_x64',
|
|
196
|
+
razerDir,
|
|
197
|
+
fetch,
|
|
198
|
+
argv.timeout || 300,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Update lock file with restored version
|
|
202
|
+
if (fs.existsSync(lockPath)) {
|
|
203
|
+
try {
|
|
204
|
+
const lockData = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
|
205
|
+
for (const [depName, version] of resolvedDeps) {
|
|
206
|
+
lockData.packages[depName] = version;
|
|
207
|
+
}
|
|
208
|
+
lockData.generatedAt = new Date().toISOString();
|
|
209
|
+
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n', 'utf8');
|
|
210
|
+
} catch {
|
|
211
|
+
// Ignore lock file errors
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* List all globally registered packages.
|
|
219
|
+
*/
|
|
220
|
+
function listLinked() {
|
|
221
|
+
const config = readConfig();
|
|
222
|
+
const linked = config.linkedPackages || {};
|
|
223
|
+
const entries = Object.entries(linked);
|
|
224
|
+
|
|
225
|
+
if (entries.length === 0) {
|
|
226
|
+
console.log('[wyvrn] No globally registered packages');
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
console.log('[wyvrn] Globally registered packages:');
|
|
231
|
+
for (const [name, rawEntry] of entries) {
|
|
232
|
+
const entry = normalizeLinkedEntry(rawEntry);
|
|
233
|
+
const effectivePath = getEffectivePath(entry);
|
|
234
|
+
const exists = fs.existsSync(effectivePath);
|
|
235
|
+
const status = exists ? '' : ' (path not found)';
|
|
236
|
+
const subdirInfo = entry.subdir ? ` [subdir: ${entry.subdir}]` : '';
|
|
237
|
+
console.log(` ${name} → ${effectivePath}${subdirInfo}${status}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Main link command handler.
|
|
243
|
+
*
|
|
244
|
+
* @param {object} argv - Parsed yargs arguments.
|
|
245
|
+
*/
|
|
246
|
+
async function link(argv) {
|
|
247
|
+
const rootDir = path.resolve(argv.root);
|
|
248
|
+
const manifestPath = path.resolve(argv.manifest);
|
|
249
|
+
|
|
250
|
+
// wyvrnpm link --list
|
|
251
|
+
if (argv.list) {
|
|
252
|
+
listLinked();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// wyvrnpm link (no args) - register current package globally
|
|
257
|
+
if (!argv.name) {
|
|
258
|
+
if (!fs.existsSync(manifestPath)) {
|
|
259
|
+
console.error(`[wyvrn] Error: no wyvrn.json found at ${manifestPath}`);
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
registerGlobal(manifestPath, argv.subdir);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// wyvrnpm link <name> <path> - direct path link
|
|
267
|
+
if (argv.name && argv.path) {
|
|
268
|
+
let targetPath = argv.path;
|
|
269
|
+
if (argv.subdir) {
|
|
270
|
+
targetPath = path.join(argv.path, argv.subdir);
|
|
271
|
+
}
|
|
272
|
+
linkToProject(argv.name, targetPath, rootDir);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// wyvrnpm link <name> - link from global registry
|
|
277
|
+
const config = readConfig();
|
|
278
|
+
const linked = config.linkedPackages || {};
|
|
279
|
+
|
|
280
|
+
if (!linked[argv.name]) {
|
|
281
|
+
console.error(`[wyvrn] Error: "${argv.name}" is not registered globally`);
|
|
282
|
+
console.error('[wyvrn] Run "wyvrnpm link" in the package directory first, or use:');
|
|
283
|
+
console.error(`[wyvrn] wyvrnpm link ${argv.name} <path>`);
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const entry = normalizeLinkedEntry(linked[argv.name]);
|
|
288
|
+
// Command-line --subdir overrides the registered subdir
|
|
289
|
+
let effectivePath;
|
|
290
|
+
if (argv.subdir) {
|
|
291
|
+
effectivePath = path.join(entry.path, argv.subdir);
|
|
292
|
+
} else {
|
|
293
|
+
effectivePath = getEffectivePath(entry);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
linkToProject(argv.name, effectivePath, rootDir);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Unlink command handler.
|
|
301
|
+
*
|
|
302
|
+
* @param {object} argv - Parsed yargs arguments.
|
|
303
|
+
*/
|
|
304
|
+
async function unlink(argv) {
|
|
305
|
+
const rootDir = path.resolve(argv.root);
|
|
306
|
+
await unlinkFromProject(argv.name, rootDir, argv.restore, argv);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
module.exports = {
|
|
310
|
+
link,
|
|
311
|
+
unlink,
|
|
312
|
+
listLinked,
|
|
313
|
+
registerGlobal,
|
|
314
|
+
linkToProject,
|
|
315
|
+
unlinkFromProject,
|
|
316
|
+
};
|