@illumine/npm-diff 0.0.1 → 0.2.0
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 +26 -0
- package/lib/index.cjs +59 -24
- package/lib/index.js +60 -26
- package/package.json +2 -1
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @illumine/npm-diff
|
|
2
|
+
|
|
3
|
+
Diff two versions of an npm package in VS Code, unminified and formatted.
|
|
4
|
+
|
|
5
|
+
## CLI
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx @illumine/npm-diff react 18.2.0 18.3.0
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Omit the versions to pick them interactively. Opens VS Code with the
|
|
12
|
+
[compare-folders](https://marketplace.visualstudio.com/items?itemName=moshfeu.compare-folders)
|
|
13
|
+
extension, so a `code` CLI on your `PATH` is required.
|
|
14
|
+
|
|
15
|
+
## Options
|
|
16
|
+
|
|
17
|
+
| Option | Description | Default |
|
|
18
|
+
| ----------------- | ---------------------------------------------- | ----------------------------- |
|
|
19
|
+
| `-r, --registry` | npm registry to query. | `https://registry.npmjs.org` |
|
|
20
|
+
| `-w, --workspace` | Directory to place the two versions. | a temp dir, removed on exit |
|
|
21
|
+
| `-u, --unminify` | Unminify sources before diffing. | off |
|
|
22
|
+
| `-p, --pattern` | Compare only files matching this glob (repeat for more). | all files |
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
MIT
|
package/lib/index.cjs
CHANGED
|
@@ -5,15 +5,17 @@ var extraTypings = require('@commander-js/extra-typings');
|
|
|
5
5
|
var zx = require('zx');
|
|
6
6
|
var path = require('path');
|
|
7
7
|
var semver = require('semver');
|
|
8
|
-
var tar = require('tar');
|
|
9
8
|
var prompts = require('@inquirer/prompts');
|
|
10
9
|
var promises = require('fs/promises');
|
|
11
10
|
var unminify = require('@illumine/unminify');
|
|
12
11
|
var oxfmt = require('oxfmt');
|
|
12
|
+
var picomatch = require('picomatch');
|
|
13
|
+
var tar = require('tar');
|
|
13
14
|
|
|
14
15
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
16
|
|
|
16
17
|
var semver__default = /*#__PURE__*/_interopDefault(semver);
|
|
18
|
+
var picomatch__default = /*#__PURE__*/_interopDefault(picomatch);
|
|
17
19
|
|
|
18
20
|
// src/error.ts
|
|
19
21
|
var CliError = class extends Error {
|
|
@@ -69,13 +71,34 @@ async function formatTree(dir) {
|
|
|
69
71
|
try {
|
|
70
72
|
const { code: formatted } = await oxfmt.format(path.basename(file), code, {
|
|
71
73
|
tabWidth: 2,
|
|
72
|
-
printWidth:
|
|
74
|
+
printWidth: 100
|
|
73
75
|
});
|
|
74
76
|
await promises.writeFile(file, formatted);
|
|
75
77
|
} catch {
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
}
|
|
81
|
+
async function unpack(tarball, dir, patterns) {
|
|
82
|
+
const isMatch = compilePattern(patterns);
|
|
83
|
+
const filter = isMatch ? (path) => {
|
|
84
|
+
const rel = path.split("/").slice(1).join("/");
|
|
85
|
+
return rel !== "" && isMatch(rel);
|
|
86
|
+
} : void 0;
|
|
87
|
+
await tar.extract({ file: tarball, cwd: dir, strip: 1, filter });
|
|
88
|
+
await promises.rm(tarball);
|
|
89
|
+
}
|
|
90
|
+
function compilePattern(patterns) {
|
|
91
|
+
if (patterns.length === 0) return void 0;
|
|
92
|
+
const include = [];
|
|
93
|
+
const ignore = [];
|
|
94
|
+
for (const pattern of patterns) {
|
|
95
|
+
const negated = pattern.startsWith("!");
|
|
96
|
+
const base = (negated ? pattern.slice(1) : pattern).replace(/\/+$/, "");
|
|
97
|
+
const target = negated ? ignore : include;
|
|
98
|
+
target.push(base, `${base}/**`);
|
|
99
|
+
}
|
|
100
|
+
return picomatch__default.default(include.length ? include : "**", { dot: true, ignore });
|
|
101
|
+
}
|
|
79
102
|
var EXTENSION_ID = "moshfeu.compare-folders";
|
|
80
103
|
async function ensureVscode() {
|
|
81
104
|
const code = await zx.which("code", { nothrow: true });
|
|
@@ -105,32 +128,41 @@ async function launchDiff(left, right) {
|
|
|
105
128
|
|
|
106
129
|
// src/npm-diff.ts
|
|
107
130
|
var NpmDiff = class {
|
|
108
|
-
constructor(pkg,
|
|
131
|
+
constructor(pkg, options = {}) {
|
|
109
132
|
this.pkg = pkg;
|
|
110
|
-
this.registry = registry;
|
|
133
|
+
this.registry = options.registry ?? DEFAULT_REGISTRY;
|
|
134
|
+
this.workdir = options.workspace ? path.resolve(options.workspace) : zx.tmpdir("npmdiff-");
|
|
135
|
+
this.clean = options.workspace === void 0;
|
|
136
|
+
this.unminify = options.unminify ?? false;
|
|
137
|
+
this.patterns = options.pattern ?? [];
|
|
111
138
|
}
|
|
112
139
|
pkg;
|
|
113
|
-
registry;
|
|
114
140
|
packument;
|
|
115
|
-
|
|
141
|
+
registry;
|
|
142
|
+
workdir;
|
|
143
|
+
clean;
|
|
144
|
+
unminify;
|
|
145
|
+
patterns;
|
|
116
146
|
async run(specA, specB) {
|
|
117
147
|
await ensureVscode();
|
|
118
148
|
this.packument = await zx.spinner(
|
|
119
149
|
`Fetching '${this.pkg}' from npm\u2026`,
|
|
120
150
|
() => fetchPackument(this.pkg, this.registry)
|
|
121
151
|
);
|
|
122
|
-
const [
|
|
123
|
-
if (
|
|
124
|
-
throw new CliError(`Both specs resolve to ${
|
|
152
|
+
const [versionA, versionB] = specA && specB ? sortVersions(this.resolve(specA), this.resolve(specB)) : await this.pickVersions(specA);
|
|
153
|
+
if (versionA === versionB) {
|
|
154
|
+
throw new CliError(`Both specs resolve to ${versionA} \u2014 there is nothing to diff.`);
|
|
125
155
|
}
|
|
126
156
|
try {
|
|
127
|
-
await this.prepare(
|
|
128
|
-
await this.prepare(
|
|
129
|
-
console.log(zx.chalk.green(`Opening VS Code to diff ${this.pkg} ${
|
|
130
|
-
await launchDiff(
|
|
131
|
-
console.log(
|
|
157
|
+
const left = await this.prepare(versionA);
|
|
158
|
+
const right = await this.prepare(versionB);
|
|
159
|
+
console.log(zx.chalk.green(`Opening VS Code to diff ${this.pkg} ${versionA} \u2194 ${versionB}`));
|
|
160
|
+
await launchDiff(left, right);
|
|
161
|
+
console.log(
|
|
162
|
+
this.clean ? zx.chalk.dim("VS Code closed \u2014 cleaning up temporary files.") : zx.chalk.dim(`VS Code closed \u2014 files kept in ${this.workdir}`)
|
|
163
|
+
);
|
|
132
164
|
} finally {
|
|
133
|
-
await zx.fs.remove(this.workdir);
|
|
165
|
+
if (this.clean) await zx.fs.remove(this.workdir);
|
|
134
166
|
}
|
|
135
167
|
}
|
|
136
168
|
/** Resolve a version or dist-tag into a concrete published version. */
|
|
@@ -160,27 +192,30 @@ var NpmDiff = class {
|
|
|
160
192
|
const dir = path.join(this.workdir, version);
|
|
161
193
|
await zx.fs.mkdirp(dir);
|
|
162
194
|
const tarball = await zx.spinner(`Downloading ${label}`, () => this.download(version, dir));
|
|
163
|
-
await zx.spinner(`Unpacking ${label}`, () =>
|
|
164
|
-
await zx.spinner(`Unminifying ${label}`, () => unminifyTree(dir));
|
|
195
|
+
await zx.spinner(`Unpacking ${label}`, () => unpack(tarball, dir, this.patterns));
|
|
196
|
+
if (this.unminify) await zx.spinner(`Unminifying ${label}`, () => unminifyTree(dir));
|
|
165
197
|
await zx.spinner(`Formatting ${label}`, () => formatTree(dir));
|
|
198
|
+
return dir;
|
|
166
199
|
}
|
|
167
200
|
async download(version, dir) {
|
|
168
201
|
const { stdout } = await zx.$`npm pack ${this.pkg}@${version} --registry ${this.registry} --pack-destination ${dir} --json`;
|
|
169
202
|
const [{ filename }] = JSON.parse(stdout);
|
|
170
203
|
return path.join(dir, filename);
|
|
171
204
|
}
|
|
172
|
-
async unpack(tarball, dir) {
|
|
173
|
-
await tar.extract({ file: tarball, cwd: dir, strip: 1 });
|
|
174
|
-
await zx.fs.remove(tarball);
|
|
175
|
-
}
|
|
176
205
|
};
|
|
177
206
|
|
|
178
207
|
// src/index.ts
|
|
179
208
|
zx.$.verbose = false;
|
|
180
209
|
var program = new extraTypings.Command();
|
|
181
|
-
program.name("npmdiff").description("Diff two versions of an npm package in VS Code, unminified and formatted").argument("<pkgname>", "npm package name (scoped names supported)").argument("[spec-a]", "a version or dist-tag").argument("[spec-b]", "a version or dist-tag").option("--registry <url>", "npm registry to query", DEFAULT_REGISTRY).
|
|
182
|
-
|
|
183
|
-
|
|
210
|
+
program.name("npmdiff").description("Diff two versions of an npm package in VS Code, unminified and formatted").argument("<pkgname>", "npm package name (scoped names supported)").argument("[spec-a]", "a version or dist-tag").argument("[spec-b]", "a version or dist-tag").option("-r, --registry <url>", "npm registry to query", DEFAULT_REGISTRY).option(
|
|
211
|
+
"-w, --workspace <dir>",
|
|
212
|
+
"directory to place the two versions; kept instead of auto-cleaned when set"
|
|
213
|
+
).option("-u, --unminify", "unminify sources before diffing", false).option(
|
|
214
|
+
"-p, --pattern <glob>",
|
|
215
|
+
"compare only files matching this glob; repeat for more",
|
|
216
|
+
(value, previous) => [...previous, value],
|
|
217
|
+
[]
|
|
218
|
+
).action((pkgname, specA, specB, options) => new NpmDiff(pkgname, options).run(specA, specB));
|
|
184
219
|
program.parseAsync().catch((error) => {
|
|
185
220
|
if (error instanceof CliError) {
|
|
186
221
|
console.error(zx.chalk.red(error.message));
|
package/lib/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from '@commander-js/extra-typings';
|
|
3
3
|
import { $, chalk, tmpdir, spinner, fs, which, glob } from 'zx';
|
|
4
|
-
import { join, basename } from 'path';
|
|
4
|
+
import { resolve, join, basename } from 'path';
|
|
5
5
|
import semver from 'semver';
|
|
6
|
-
import { extract } from 'tar';
|
|
7
6
|
import { confirm, search } from '@inquirer/prompts';
|
|
8
|
-
import { readFile, writeFile } from 'fs/promises';
|
|
7
|
+
import { rm, readFile, writeFile } from 'fs/promises';
|
|
9
8
|
import { unminify } from '@illumine/unminify';
|
|
10
9
|
import { format } from 'oxfmt';
|
|
10
|
+
import picomatch from 'picomatch';
|
|
11
|
+
import { extract } from 'tar';
|
|
11
12
|
|
|
12
13
|
// src/error.ts
|
|
13
14
|
var CliError = class extends Error {
|
|
@@ -63,13 +64,34 @@ async function formatTree(dir) {
|
|
|
63
64
|
try {
|
|
64
65
|
const { code: formatted } = await format(basename(file), code, {
|
|
65
66
|
tabWidth: 2,
|
|
66
|
-
printWidth:
|
|
67
|
+
printWidth: 100
|
|
67
68
|
});
|
|
68
69
|
await writeFile(file, formatted);
|
|
69
70
|
} catch {
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
}
|
|
74
|
+
async function unpack(tarball, dir, patterns) {
|
|
75
|
+
const isMatch = compilePattern(patterns);
|
|
76
|
+
const filter = isMatch ? (path) => {
|
|
77
|
+
const rel = path.split("/").slice(1).join("/");
|
|
78
|
+
return rel !== "" && isMatch(rel);
|
|
79
|
+
} : void 0;
|
|
80
|
+
await extract({ file: tarball, cwd: dir, strip: 1, filter });
|
|
81
|
+
await rm(tarball);
|
|
82
|
+
}
|
|
83
|
+
function compilePattern(patterns) {
|
|
84
|
+
if (patterns.length === 0) return void 0;
|
|
85
|
+
const include = [];
|
|
86
|
+
const ignore = [];
|
|
87
|
+
for (const pattern of patterns) {
|
|
88
|
+
const negated = pattern.startsWith("!");
|
|
89
|
+
const base = (negated ? pattern.slice(1) : pattern).replace(/\/+$/, "");
|
|
90
|
+
const target = negated ? ignore : include;
|
|
91
|
+
target.push(base, `${base}/**`);
|
|
92
|
+
}
|
|
93
|
+
return picomatch(include.length ? include : "**", { dot: true, ignore });
|
|
94
|
+
}
|
|
73
95
|
var EXTENSION_ID = "moshfeu.compare-folders";
|
|
74
96
|
async function ensureVscode() {
|
|
75
97
|
const code = await which("code", { nothrow: true });
|
|
@@ -99,32 +121,41 @@ async function launchDiff(left, right) {
|
|
|
99
121
|
|
|
100
122
|
// src/npm-diff.ts
|
|
101
123
|
var NpmDiff = class {
|
|
102
|
-
constructor(pkg,
|
|
124
|
+
constructor(pkg, options = {}) {
|
|
103
125
|
this.pkg = pkg;
|
|
104
|
-
this.registry = registry;
|
|
126
|
+
this.registry = options.registry ?? DEFAULT_REGISTRY;
|
|
127
|
+
this.workdir = options.workspace ? resolve(options.workspace) : tmpdir("npmdiff-");
|
|
128
|
+
this.clean = options.workspace === void 0;
|
|
129
|
+
this.unminify = options.unminify ?? false;
|
|
130
|
+
this.patterns = options.pattern ?? [];
|
|
105
131
|
}
|
|
106
132
|
pkg;
|
|
107
|
-
registry;
|
|
108
133
|
packument;
|
|
109
|
-
|
|
134
|
+
registry;
|
|
135
|
+
workdir;
|
|
136
|
+
clean;
|
|
137
|
+
unminify;
|
|
138
|
+
patterns;
|
|
110
139
|
async run(specA, specB) {
|
|
111
140
|
await ensureVscode();
|
|
112
141
|
this.packument = await spinner(
|
|
113
142
|
`Fetching '${this.pkg}' from npm\u2026`,
|
|
114
143
|
() => fetchPackument(this.pkg, this.registry)
|
|
115
144
|
);
|
|
116
|
-
const [
|
|
117
|
-
if (
|
|
118
|
-
throw new CliError(`Both specs resolve to ${
|
|
145
|
+
const [versionA, versionB] = specA && specB ? sortVersions(this.resolve(specA), this.resolve(specB)) : await this.pickVersions(specA);
|
|
146
|
+
if (versionA === versionB) {
|
|
147
|
+
throw new CliError(`Both specs resolve to ${versionA} \u2014 there is nothing to diff.`);
|
|
119
148
|
}
|
|
120
149
|
try {
|
|
121
|
-
await this.prepare(
|
|
122
|
-
await this.prepare(
|
|
123
|
-
console.log(chalk.green(`Opening VS Code to diff ${this.pkg} ${
|
|
124
|
-
await launchDiff(
|
|
125
|
-
console.log(
|
|
150
|
+
const left = await this.prepare(versionA);
|
|
151
|
+
const right = await this.prepare(versionB);
|
|
152
|
+
console.log(chalk.green(`Opening VS Code to diff ${this.pkg} ${versionA} \u2194 ${versionB}`));
|
|
153
|
+
await launchDiff(left, right);
|
|
154
|
+
console.log(
|
|
155
|
+
this.clean ? chalk.dim("VS Code closed \u2014 cleaning up temporary files.") : chalk.dim(`VS Code closed \u2014 files kept in ${this.workdir}`)
|
|
156
|
+
);
|
|
126
157
|
} finally {
|
|
127
|
-
await fs.remove(this.workdir);
|
|
158
|
+
if (this.clean) await fs.remove(this.workdir);
|
|
128
159
|
}
|
|
129
160
|
}
|
|
130
161
|
/** Resolve a version or dist-tag into a concrete published version. */
|
|
@@ -154,27 +185,30 @@ var NpmDiff = class {
|
|
|
154
185
|
const dir = join(this.workdir, version);
|
|
155
186
|
await fs.mkdirp(dir);
|
|
156
187
|
const tarball = await spinner(`Downloading ${label}`, () => this.download(version, dir));
|
|
157
|
-
await spinner(`Unpacking ${label}`, () =>
|
|
158
|
-
await spinner(`Unminifying ${label}`, () => unminifyTree(dir));
|
|
188
|
+
await spinner(`Unpacking ${label}`, () => unpack(tarball, dir, this.patterns));
|
|
189
|
+
if (this.unminify) await spinner(`Unminifying ${label}`, () => unminifyTree(dir));
|
|
159
190
|
await spinner(`Formatting ${label}`, () => formatTree(dir));
|
|
191
|
+
return dir;
|
|
160
192
|
}
|
|
161
193
|
async download(version, dir) {
|
|
162
194
|
const { stdout } = await $`npm pack ${this.pkg}@${version} --registry ${this.registry} --pack-destination ${dir} --json`;
|
|
163
195
|
const [{ filename }] = JSON.parse(stdout);
|
|
164
196
|
return join(dir, filename);
|
|
165
197
|
}
|
|
166
|
-
async unpack(tarball, dir) {
|
|
167
|
-
await extract({ file: tarball, cwd: dir, strip: 1 });
|
|
168
|
-
await fs.remove(tarball);
|
|
169
|
-
}
|
|
170
198
|
};
|
|
171
199
|
|
|
172
200
|
// src/index.ts
|
|
173
201
|
$.verbose = false;
|
|
174
202
|
var program = new Command();
|
|
175
|
-
program.name("npmdiff").description("Diff two versions of an npm package in VS Code, unminified and formatted").argument("<pkgname>", "npm package name (scoped names supported)").argument("[spec-a]", "a version or dist-tag").argument("[spec-b]", "a version or dist-tag").option("--registry <url>", "npm registry to query", DEFAULT_REGISTRY).
|
|
176
|
-
|
|
177
|
-
|
|
203
|
+
program.name("npmdiff").description("Diff two versions of an npm package in VS Code, unminified and formatted").argument("<pkgname>", "npm package name (scoped names supported)").argument("[spec-a]", "a version or dist-tag").argument("[spec-b]", "a version or dist-tag").option("-r, --registry <url>", "npm registry to query", DEFAULT_REGISTRY).option(
|
|
204
|
+
"-w, --workspace <dir>",
|
|
205
|
+
"directory to place the two versions; kept instead of auto-cleaned when set"
|
|
206
|
+
).option("-u, --unminify", "unminify sources before diffing", false).option(
|
|
207
|
+
"-p, --pattern <glob>",
|
|
208
|
+
"compare only files matching this glob; repeat for more",
|
|
209
|
+
(value, previous) => [...previous, value],
|
|
210
|
+
[]
|
|
211
|
+
).action((pkgname, specA, specB, options) => new NpmDiff(pkgname, options).run(specA, specB));
|
|
178
212
|
program.parseAsync().catch((error) => {
|
|
179
213
|
if (error instanceof CliError) {
|
|
180
214
|
console.error(chalk.red(error.message));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@illumine/npm-diff",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Diff two versions of an npm package in VS Code, unminified and formatted",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@commander-js/extra-typings": "^15.0.0",
|
|
37
37
|
"commander": "^15.0.0",
|
|
38
38
|
"oxfmt": "~0.53.0",
|
|
39
|
+
"picomatch": "^4.0.5",
|
|
39
40
|
"semver": "^7.8.5",
|
|
40
41
|
"tar": "^7.5.19",
|
|
41
42
|
"zx": "^8.8.5",
|