jpm-pkg 1.0.3 → 1.0.5
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/bin/jpm.js +7 -1
- package/package.json +1 -1
- package/src/commands/audit.js +70 -41
- package/src/commands/base-command.js +62 -0
- package/src/commands/config.js +94 -51
- package/src/commands/info.js +111 -64
- package/src/commands/init.js +117 -72
- package/src/commands/install.js +121 -112
- package/src/commands/list.js +126 -80
- package/src/commands/publish.js +155 -124
- package/src/commands/run.js +86 -52
- package/src/commands/search.js +56 -32
- package/src/commands/uninstall.js +47 -30
- package/src/commands/update.js +92 -51
- package/src/commands/x.js +134 -96
- package/src/core/cache.js +188 -87
- package/src/core/lockfile.js +73 -26
- package/src/core/package-json.js +119 -1
- package/src/core/registry.js +182 -143
- package/src/core/resolver.js +60 -36
package/src/core/lockfile.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const path = require('node:path');
|
|
4
|
+
const fs = require('node:fs');
|
|
4
5
|
const { readJSONSafe, writeJSON } = require('../utils/fs');
|
|
5
6
|
const { hashString } = require('../security/integrity');
|
|
6
7
|
|
|
@@ -8,28 +9,28 @@ const LOCK_VERSION = 1;
|
|
|
8
9
|
const LOCK_FILE = 'jpm-lock.json';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
* jpm-lock.json
|
|
12
|
-
*
|
|
13
|
-
* "lockVersion": 1,
|
|
14
|
-
* "packages": {
|
|
15
|
-
* "express@4.18.2": {
|
|
16
|
-
* "name": "express",
|
|
17
|
-
* "version": "4.18.2",
|
|
18
|
-
* "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
|
19
|
-
* "integrity": "sha512-...",
|
|
20
|
-
* "dependencies": { "accepts": "^1.3.8", ... }
|
|
21
|
-
* },
|
|
22
|
-
* ...
|
|
23
|
-
* }
|
|
24
|
-
* }
|
|
12
|
+
* Manages the jpm-lock.json file for deterministic installations.
|
|
13
|
+
* The lockfile stores exact versions and integrity hashes for all dependencies.
|
|
25
14
|
*/
|
|
26
|
-
|
|
27
15
|
class Lockfile {
|
|
16
|
+
/**
|
|
17
|
+
* Creates an instance of the Lockfile.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} projectRoot - The absolute path to the project root directory
|
|
20
|
+
*/
|
|
28
21
|
constructor(projectRoot) {
|
|
22
|
+
/** @type {string} */
|
|
29
23
|
this.filePath = path.join(projectRoot, LOCK_FILE);
|
|
24
|
+
/** @type {Object} */
|
|
30
25
|
this._data = this._load();
|
|
31
26
|
}
|
|
32
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Loads the lockfile data from disk or returns a default structure.
|
|
30
|
+
*
|
|
31
|
+
* @returns {Object} The lockfile data
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
33
34
|
_load() {
|
|
34
35
|
const data = readJSONSafe(this.filePath, null);
|
|
35
36
|
if (!data) return { lockVersion: LOCK_VERSION, packages: {} };
|
|
@@ -37,7 +38,11 @@ class Lockfile {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
|
-
*
|
|
41
|
+
* Builds or updates the lockfile data from a resolved dependency map.
|
|
42
|
+
* Sorts keys for consistent output.
|
|
43
|
+
*
|
|
44
|
+
* @param {Map<string, Object>} resolvedMap - Map of resolved package metadata
|
|
45
|
+
* @returns {this} The current instance for chaining
|
|
41
46
|
*/
|
|
42
47
|
update(resolvedMap) {
|
|
43
48
|
const packages = {};
|
|
@@ -65,7 +70,9 @@ class Lockfile {
|
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
/**
|
|
68
|
-
* Verifies
|
|
73
|
+
* Verifies the cryptographic integrity of the lockfile packages.
|
|
74
|
+
*
|
|
75
|
+
* @returns {boolean} True if the integrity hash matches the current package data
|
|
69
76
|
*/
|
|
70
77
|
verify() {
|
|
71
78
|
if (!this._data.integrity || !this._data.packages) return true;
|
|
@@ -73,7 +80,14 @@ class Lockfile {
|
|
|
73
80
|
return actual === this._data.integrity;
|
|
74
81
|
}
|
|
75
82
|
|
|
76
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* Adds or updates a single package entry in the lockfile data.
|
|
85
|
+
*
|
|
86
|
+
* @param {string} name - Package name
|
|
87
|
+
* @param {string} version - Package version
|
|
88
|
+
* @param {Object} meta - Package metadata
|
|
89
|
+
* @returns {this} The current instance for chaining
|
|
90
|
+
*/
|
|
77
91
|
setPackage(name, version, meta) {
|
|
78
92
|
const key = `${name}@${version}`;
|
|
79
93
|
this._data.packages[key] = {
|
|
@@ -87,42 +101,75 @@ class Lockfile {
|
|
|
87
101
|
return this;
|
|
88
102
|
}
|
|
89
103
|
|
|
90
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Removes a package entry from the lockfile data.
|
|
106
|
+
*
|
|
107
|
+
* @param {string} name - Package name
|
|
108
|
+
* @param {string} version - Package version
|
|
109
|
+
* @returns {this} The current instance for chaining
|
|
110
|
+
*/
|
|
91
111
|
removePackage(name, version) {
|
|
92
112
|
const key = `${name}@${version}`;
|
|
93
113
|
delete this._data.packages[key];
|
|
94
114
|
return this;
|
|
95
115
|
}
|
|
96
116
|
|
|
97
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Retrieves a specific package entry from the lockfile data.
|
|
119
|
+
*
|
|
120
|
+
* @param {string} name - Package name
|
|
121
|
+
* @param {string} version - Package version
|
|
122
|
+
* @returns {Object|null} The package entry or null if not found
|
|
123
|
+
*/
|
|
98
124
|
getPackage(name, version) {
|
|
99
125
|
return this._data.packages[`${name}@${version}`] || null;
|
|
100
126
|
}
|
|
101
127
|
|
|
102
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* Checks if the lockfile contains a specific package version.
|
|
130
|
+
*
|
|
131
|
+
* @param {string} name - Package name
|
|
132
|
+
* @param {string} version - Package version
|
|
133
|
+
* @returns {boolean} True if the package version exists
|
|
134
|
+
*/
|
|
103
135
|
hasPackage(name, version) {
|
|
104
136
|
return Boolean(this._data.packages[`${name}@${version}`]);
|
|
105
137
|
}
|
|
106
138
|
|
|
107
|
-
/**
|
|
139
|
+
/**
|
|
140
|
+
* Returns an array of all package entries stored in the lockfile.
|
|
141
|
+
*
|
|
142
|
+
* @returns {Object[]} Array of package entry objects
|
|
143
|
+
*/
|
|
108
144
|
allPackages() {
|
|
109
145
|
return Object.values(this._data.packages);
|
|
110
146
|
}
|
|
111
147
|
|
|
112
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* Writes the current lockfile data to disk.
|
|
150
|
+
*
|
|
151
|
+
* @returns {this} The current instance for chaining
|
|
152
|
+
*/
|
|
113
153
|
save() {
|
|
114
154
|
writeJSON(this.filePath, this._data, 2);
|
|
115
155
|
return this;
|
|
116
156
|
}
|
|
117
157
|
|
|
118
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* Returns the raw lockfile data structure.
|
|
160
|
+
* @type {Object}
|
|
161
|
+
*/
|
|
119
162
|
get data() { return this._data; }
|
|
120
163
|
|
|
121
|
-
/**
|
|
164
|
+
/**
|
|
165
|
+
* Checks if the lockfile exists on the filesystem.
|
|
166
|
+
*
|
|
167
|
+
* @returns {boolean} True if the lockfile exists
|
|
168
|
+
*/
|
|
122
169
|
exists() {
|
|
123
|
-
const fs = require('node:fs');
|
|
124
170
|
return fs.existsSync(this.filePath);
|
|
125
171
|
}
|
|
126
172
|
}
|
|
127
173
|
|
|
128
174
|
module.exports = Lockfile;
|
|
175
|
+
|
package/src/core/package-json.js
CHANGED
|
@@ -3,33 +3,69 @@
|
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
const fs = require('node:fs');
|
|
5
5
|
const { readJSONSafe, writeJSON, findPackageJson } = require('../utils/fs');
|
|
6
|
-
const logger = require('../utils/logger');
|
|
7
6
|
|
|
8
7
|
const REQUIRED_FIELDS = ['name', 'version'];
|
|
9
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Represents a Node.js package.json file.
|
|
11
|
+
* Provides methods for reading, validating, mutating, and persisting package metadata.
|
|
12
|
+
*/
|
|
10
13
|
class PackageJSON {
|
|
14
|
+
/**
|
|
15
|
+
* Creates an instance of the PackageJSON.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} filePath - Absolute path to the package.json file
|
|
18
|
+
*/
|
|
11
19
|
constructor(filePath) {
|
|
20
|
+
/** @type {string} */
|
|
12
21
|
this.filePath = filePath;
|
|
22
|
+
/** @type {Object} */
|
|
13
23
|
this._data = this._load();
|
|
14
24
|
}
|
|
15
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Creates a PackageJSON instance from a directory path.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} dir - Directory containing the package.json
|
|
30
|
+
* @returns {PackageJSON}
|
|
31
|
+
*/
|
|
16
32
|
static fromDir(dir) {
|
|
17
33
|
const f = path.join(dir, 'package.json');
|
|
18
34
|
return new PackageJSON(f);
|
|
19
35
|
}
|
|
20
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Finds and loads the nearest package.json starting from a directory.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} [startDir=process.cwd()] - Directory to start the search from
|
|
41
|
+
* @returns {PackageJSON}
|
|
42
|
+
* @throws {Error} If no package.json is found in the directory tree
|
|
43
|
+
*/
|
|
21
44
|
static find(startDir = process.cwd()) {
|
|
22
45
|
const f = findPackageJson(startDir);
|
|
23
46
|
if (!f) throw new Error('No package.json found in this directory or any parent.');
|
|
24
47
|
return new PackageJSON(f);
|
|
25
48
|
}
|
|
26
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Loads the package.json data from disk.
|
|
52
|
+
*
|
|
53
|
+
* @returns {Object} The package data or a default structure if not found
|
|
54
|
+
* @private
|
|
55
|
+
*/
|
|
27
56
|
_load() {
|
|
28
57
|
const data = readJSONSafe(this.filePath, null);
|
|
29
58
|
if (!data) return this._empty();
|
|
30
59
|
return data;
|
|
31
60
|
}
|
|
32
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Returns a default package.json structure.
|
|
64
|
+
* Used when creating a new package or when the file is missing/invalid.
|
|
65
|
+
*
|
|
66
|
+
* @returns {Object}
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
33
69
|
_empty() {
|
|
34
70
|
return {
|
|
35
71
|
name: path.basename(path.dirname(this.filePath || process.cwd())),
|
|
@@ -47,22 +83,57 @@ class PackageJSON {
|
|
|
47
83
|
|
|
48
84
|
// ── Accessors ────────────────────────────────────────────────────────────────
|
|
49
85
|
|
|
86
|
+
/** @type {string} */
|
|
50
87
|
get name() { return this._data.name; }
|
|
88
|
+
|
|
89
|
+
/** @type {string} */
|
|
51
90
|
get version() { return this._data.version; }
|
|
91
|
+
|
|
92
|
+
/** @type {Object.<string, string>} */
|
|
52
93
|
get dependencies() { return this._data.dependencies || {}; }
|
|
94
|
+
|
|
95
|
+
/** @type {Object.<string, string>} */
|
|
53
96
|
get devDependencies() { return this._data.devDependencies || {}; }
|
|
97
|
+
|
|
98
|
+
/** @type {Object.<string, string>} */
|
|
54
99
|
get peerDependencies() { return this._data.peerDependencies || {}; }
|
|
100
|
+
|
|
101
|
+
/** @type {Object.<string, string>} */
|
|
55
102
|
get optionalDeps() { return this._data.optionalDependencies || {}; }
|
|
103
|
+
|
|
104
|
+
/** @type {Object.<string, string>} */
|
|
56
105
|
get scripts() { return this._data.scripts || {}; }
|
|
106
|
+
|
|
107
|
+
/** @type {string[]} */
|
|
57
108
|
get workspaces() { return this._data.workspaces || []; }
|
|
109
|
+
|
|
110
|
+
/** @type {string} */
|
|
58
111
|
get main() { return this._data.main || 'index.js'; }
|
|
112
|
+
|
|
113
|
+
/** @type {Object.<string, string>} */
|
|
59
114
|
get bin() { return this._data.bin || {}; }
|
|
115
|
+
|
|
116
|
+
/** @type {Object.<string, string>} */
|
|
60
117
|
get engines() { return this._data.engines || {}; }
|
|
118
|
+
|
|
119
|
+
/** @type {Object} */
|
|
61
120
|
get data() { return this._data; }
|
|
121
|
+
|
|
122
|
+
/** @type {string} */
|
|
62
123
|
get dir() { return path.dirname(this.filePath); }
|
|
63
124
|
|
|
64
125
|
// ── Mutations ────────────────────────────────────────────────────────────────
|
|
65
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Adds a dependency to the package.json.
|
|
129
|
+
*
|
|
130
|
+
* @param {string} name - Package name
|
|
131
|
+
* @param {string} version - Package version
|
|
132
|
+
* @param {Object} [options={}] - Dependency options
|
|
133
|
+
* @param {boolean} [options.dev=false] - Whether to add as a devDependency
|
|
134
|
+
* @param {boolean} [options.exact=false] - Whether to use the exact version instead of a caret range
|
|
135
|
+
* @returns {this} The current instance for chaining
|
|
136
|
+
*/
|
|
66
137
|
addDependency(name, version, { dev = false, exact = false } = {}) {
|
|
67
138
|
const range = exact ? version : `^${version}`;
|
|
68
139
|
const key = dev ? 'devDependencies' : 'dependencies';
|
|
@@ -71,6 +142,12 @@ class PackageJSON {
|
|
|
71
142
|
return this;
|
|
72
143
|
}
|
|
73
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Removes a dependency from all dependency sections.
|
|
147
|
+
*
|
|
148
|
+
* @param {string} name - Package name to remove
|
|
149
|
+
* @returns {this} The current instance for chaining
|
|
150
|
+
*/
|
|
74
151
|
removeDependency(name) {
|
|
75
152
|
for (const key of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']) {
|
|
76
153
|
if (this._data[key]) delete this._data[key][name];
|
|
@@ -78,16 +155,36 @@ class PackageJSON {
|
|
|
78
155
|
return this;
|
|
79
156
|
}
|
|
80
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Sets a top-level field in the package.json.
|
|
160
|
+
*
|
|
161
|
+
* @param {string} key - Field name
|
|
162
|
+
* @param {*} value - Field value
|
|
163
|
+
* @returns {this} The current instance for chaining
|
|
164
|
+
*/
|
|
81
165
|
setField(key, value) {
|
|
82
166
|
this._data[key] = value;
|
|
83
167
|
return this;
|
|
84
168
|
}
|
|
85
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Sets the package version.
|
|
172
|
+
*
|
|
173
|
+
* @param {string} version - New version string
|
|
174
|
+
* @returns {this} The current instance for chaining
|
|
175
|
+
*/
|
|
86
176
|
setVersion(version) {
|
|
87
177
|
this._data.version = version;
|
|
88
178
|
return this;
|
|
89
179
|
}
|
|
90
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Adds or updates a script in the scripts section.
|
|
183
|
+
*
|
|
184
|
+
* @param {string} name - Script name
|
|
185
|
+
* @param {string} command - Command to execute
|
|
186
|
+
* @returns {this} The current instance for chaining
|
|
187
|
+
*/
|
|
91
188
|
addScript(name, command) {
|
|
92
189
|
if (!this._data.scripts) this._data.scripts = {};
|
|
93
190
|
this._data.scripts[name] = command;
|
|
@@ -96,6 +193,11 @@ class PackageJSON {
|
|
|
96
193
|
|
|
97
194
|
// ── Validation ────────────────────────────────────────────────────────────────
|
|
98
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Validates the internal package data against basic npm requirements.
|
|
198
|
+
*
|
|
199
|
+
* @returns {string[]} Array of validation error messages
|
|
200
|
+
*/
|
|
99
201
|
validate() {
|
|
100
202
|
const errors = [];
|
|
101
203
|
for (const field of REQUIRED_FIELDS) {
|
|
@@ -112,15 +214,30 @@ class PackageJSON {
|
|
|
112
214
|
|
|
113
215
|
// ── Persistence ──────────────────────────────────────────────────────────────
|
|
114
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Writes the current package data to disk.
|
|
219
|
+
*
|
|
220
|
+
* @returns {this} The current instance for chaining
|
|
221
|
+
*/
|
|
115
222
|
save() {
|
|
116
223
|
writeJSON(this.filePath, this._data, 2);
|
|
117
224
|
return this;
|
|
118
225
|
}
|
|
119
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Checks if the package.json file exists on the filesystem.
|
|
229
|
+
*
|
|
230
|
+
* @returns {boolean}
|
|
231
|
+
*/
|
|
120
232
|
exists() {
|
|
121
233
|
return fs.existsSync(this.filePath);
|
|
122
234
|
}
|
|
123
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Returns a combined map of all dependencies (prod, dev, and optional).
|
|
238
|
+
*
|
|
239
|
+
* @returns {Object.<string, string>}
|
|
240
|
+
*/
|
|
124
241
|
allDeps() {
|
|
125
242
|
return {
|
|
126
243
|
...this.dependencies,
|
|
@@ -131,3 +248,4 @@ class PackageJSON {
|
|
|
131
248
|
}
|
|
132
249
|
|
|
133
250
|
module.exports = PackageJSON;
|
|
251
|
+
|