@adobe/sizewatcher 1.2.1 → 1.4.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/CHANGELOG.md +38 -0
- package/README.md +77 -44
- package/lib/checkout.js +58 -43
- package/lib/comparators/custom.js +2 -2
- package/lib/comparators/git.js +43 -15
- package/lib/comparators/node_modules.js +6 -5
- package/lib/compare.js +24 -9
- package/lib/config.js +2 -2
- package/lib/render.js +2 -2
- package/lib/size.js +68 -0
- package/lib/sizewatcher.js +1 -1
- package/package.json +27 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.0
|
|
4
|
+
|
|
5
|
+
Major changes:
|
|
6
|
+
|
|
7
|
+
- [#106](https://github.com/adobe/sizewatcher/issues/106) Require at least node 18. Was 12 before. Node 18 is last version we can make work with being a CommonJS module.
|
|
8
|
+
|
|
9
|
+
Improvements:
|
|
10
|
+
|
|
11
|
+
- Upgrade dependencies (many major bumps)
|
|
12
|
+
- Added renovate
|
|
13
|
+
- [#98](https://github.com/adobe/sizewatcher/pull/98) CI: drop `.travis.yml`, was not running on Travis CI for years anyway
|
|
14
|
+
- [#101](https://github.com/adobe/sizewatcher/pull/101) fixed package.json using `npm pkg fix`
|
|
15
|
+
- CI: various CI improvements
|
|
16
|
+
|
|
17
|
+
Fixes:
|
|
18
|
+
|
|
19
|
+
- [#108](https://github.com/adobe/sizewatcher/pull/108) [#113](https://github.com/adobe/sizewatcher/pull/113) Git comparator reporting incorrect deltas. now count files in checkout instead of .git folder for consistent sizes
|
|
20
|
+
- [#112](https://github.com/adobe/sizewatcher/pull/112) Node-modules: replace broken cost-of-modules with howfat for better largest modules reporting
|
|
21
|
+
- [#97](https://github.com/adobe/sizewatcher/pull/97) Fix node version 20+ test failures
|
|
22
|
+
|
|
23
|
+
## 1.3.0
|
|
24
|
+
|
|
25
|
+
Improvements:
|
|
26
|
+
|
|
27
|
+
- [#87](https://github.com/adobe/sizewatcher/issues/87) Drop node 10 support
|
|
28
|
+
- Various dependency updates
|
|
29
|
+
|
|
30
|
+
## 1.2.1
|
|
31
|
+
|
|
32
|
+
Important bug fixes.
|
|
33
|
+
|
|
34
|
+
Fixes:
|
|
35
|
+
|
|
36
|
+
- [#63](https://github.com/adobe/sizewatcher/issues/63) fix and improve git checkout logic
|
|
37
|
+
- [#62](https://github.com/adobe/sizewatcher/issues/62) fix failure on fork PRs
|
|
38
|
+
- [#53](https://github.com/adobe/sizewatcher/issues/53) sizewatcher fails if there is no "package.json" file in the main branch, or if a package.json has no dependencies and thus node_modules is missing
|
|
39
|
+
- [#74](https://github.com/adobe/sizewatcher/issues/74) improve delta calculation (showed weird negative values in some cases)
|
|
40
|
+
|
|
3
41
|
## 1.2.0
|
|
4
42
|
|
|
5
43
|
Improvements:
|
package/README.md
CHANGED
|
@@ -33,6 +33,7 @@ While any custom file or folder path can be measured via configuration, various
|
|
|
33
33
|
|
|
34
34
|
`sizewatcher` runs as part of your CI and reports results as comment on the pull request or as github commit status (optional), allowing to block PRs if a certain threshold was exceeded.
|
|
35
35
|
|
|
36
|
+
|
|
36
37
|
This is an example of a `sizewatcher` Github PR comment with a failure (ignore the small numbers):
|
|
37
38
|
|
|
38
39
|
---
|
|
@@ -125,7 +126,7 @@ By default `sizewatcher` will
|
|
|
125
126
|
|
|
126
127
|
## Requirements
|
|
127
128
|
|
|
128
|
-
- [Nodejs](https://nodejs.org) version
|
|
129
|
+
- [Nodejs](https://nodejs.org) version 18+ (since 1.4.0)
|
|
129
130
|
- recommended to use latest stable version "LTS"
|
|
130
131
|
- Github or Github Enterprise
|
|
131
132
|
- if you want to run it on pull requests
|
|
@@ -137,12 +138,14 @@ By default `sizewatcher` will
|
|
|
137
138
|
|
|
138
139
|
## Installation
|
|
139
140
|
|
|
140
|
-
You can install the `sizewatcher` tool locally for testing or checking your changes before committing:
|
|
141
|
+
Local: You can install the `sizewatcher` tool locally for testing or checking your changes before committing:
|
|
141
142
|
|
|
142
143
|
```
|
|
143
144
|
npm install -g @adobe/sizewatcher
|
|
144
145
|
```
|
|
145
146
|
|
|
147
|
+
For setup in Continuous Integration: see [CI Setup](#ci-setup) section.
|
|
148
|
+
|
|
146
149
|
## Usage
|
|
147
150
|
|
|
148
151
|
When run locally, `sizewatcher` will output its results on the command line.
|
|
@@ -230,7 +233,7 @@ npx @adobe/sizewatcher
|
|
|
230
233
|
This command will always use the latest published version. In some cases it might be (temporarily) desireable to stick to a certain version, which can be achieved using:
|
|
231
234
|
|
|
232
235
|
```
|
|
233
|
-
npx @adobe/sizewatcher@1.
|
|
236
|
+
npx @adobe/sizewatcher@1.2.1
|
|
234
237
|
```
|
|
235
238
|
|
|
236
239
|
#### GITHUB_TOKEN
|
|
@@ -274,11 +277,11 @@ jobs:
|
|
|
274
277
|
runs-on: ubuntu-latest
|
|
275
278
|
|
|
276
279
|
steps:
|
|
277
|
-
- uses: actions/checkout@
|
|
280
|
+
- uses: actions/checkout@v4
|
|
278
281
|
- name: Install Node.js
|
|
279
|
-
uses: actions/setup-node@
|
|
282
|
+
uses: actions/setup-node@v4
|
|
280
283
|
with:
|
|
281
|
-
node-version: '
|
|
284
|
+
node-version: 'lts/*'
|
|
282
285
|
# ---------- this runs sizewatcher ------------
|
|
283
286
|
- run: npx @adobe/sizewatcher
|
|
284
287
|
env:
|
|
@@ -289,7 +292,7 @@ jobs:
|
|
|
289
292
|
|
|
290
293
|
For [Travis CI](https://travis-ci.org) you need to
|
|
291
294
|
- use `language: node_js`
|
|
292
|
-
- if you already use a different language, find a way to ensure Nodejs
|
|
295
|
+
- if you already use a different language, find a way to ensure Nodejs 12+ is installed
|
|
293
296
|
- under `script` run `npx @adobe/sizewatcher`
|
|
294
297
|
- set a secret environment variable `GITHUB_TOKEN` in the [Travis repository settings](https://docs.travis-ci.com/user/environment-variables/#defining-variables-in-repository-settings) with a github token with permission to comment on PRs and reporting commit statuses for the repository
|
|
295
298
|
|
|
@@ -309,7 +312,7 @@ script:
|
|
|
309
312
|
### CircleCI
|
|
310
313
|
|
|
311
314
|
For [CircleCI](https://circleci.com) you need to
|
|
312
|
-
- use a docker image with Nodejs
|
|
315
|
+
- use a docker image with Nodejs 12+ installed
|
|
313
316
|
- alternatively install [using nvm](https://www.google.com/search?q=circleci+use+nvm)
|
|
314
317
|
- run `npx @adobe/sizewatcher`
|
|
315
318
|
- set a secret environment variable `GITHUB_TOKEN` in the [CircleCI project settings](https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-project) (or in a [Context](https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-context)) with a github token with permission to comment on PRs and reporting commit statuses for the repository
|
|
@@ -334,7 +337,7 @@ jobs:
|
|
|
334
337
|
|
|
335
338
|
This is not tested well but might work.
|
|
336
339
|
|
|
337
|
-
Ensure Nodejs
|
|
340
|
+
Ensure Nodejs 12+ is installed.
|
|
338
341
|
|
|
339
342
|
Set these environment variables in the CI job:
|
|
340
343
|
|
|
@@ -432,23 +435,33 @@ comparators:
|
|
|
432
435
|
|
|
433
436
|
### git
|
|
434
437
|
|
|
435
|
-
Compares the size of the git repository by measuring the `.git` folder. Useful to detect if large files are added to the repo.
|
|
438
|
+
Compares the size of the git repository by measuring the total size of the checked out folder (without the `.git` folder or any build artifacts since it runs first on a clean checkout). Useful to detect if large files are added to the repo.
|
|
436
439
|
|
|
437
440
|
Name: `git`
|
|
438
441
|
|
|
439
442
|
Trigger: Runs if a `.git` directory is found.
|
|
440
443
|
|
|
441
|
-
Details: Shows the largest files (git objects) added in the PR:
|
|
444
|
+
Details: Shows the largest files (git objects) in the entire repository and added in the PR:
|
|
442
445
|
|
|
443
446
|
---
|
|
444
|
-
Largest files
|
|
447
|
+
Largest files:
|
|
445
448
|
```
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
449
|
+
Largest files in repository checkout:
|
|
450
|
+
|
|
451
|
+
356KiB package-lock.json
|
|
452
|
+
26KiB README.md
|
|
453
|
+
11KiB LICENSE
|
|
454
|
+
5.8KiB lib/compare.js
|
|
455
|
+
5.8KiB lib/checkout.js
|
|
456
|
+
5.0KiB test/mocha-capture-console.js
|
|
457
|
+
4.7KiB test/cli.test.js
|
|
458
|
+
4.5KiB lib/report.js
|
|
459
|
+
4.3KiB test/config.test.js
|
|
460
|
+
3.8KiB lib/render.js
|
|
461
|
+
|
|
462
|
+
Largest files among new changes:
|
|
463
|
+
|
|
464
|
+
3.0KiB CHANGELOG.md
|
|
452
465
|
```
|
|
453
466
|
---
|
|
454
467
|
|
|
@@ -460,34 +473,37 @@ Name: `node_modules`
|
|
|
460
473
|
|
|
461
474
|
Trigger: Runs if a `package.json` is found.
|
|
462
475
|
|
|
463
|
-
Details: Prints the largest modules using [
|
|
476
|
+
Details: Prints the largest modules using [howfat](https://www.npmjs.com/package/howfat):
|
|
464
477
|
|
|
465
478
|
---
|
|
466
|
-
Largest node modules:
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
479
|
+
Largest production node modules:
|
|
480
|
+
```
|
|
481
|
+
@adobe/sizewatcher@1.3.0 (67 deps, 21.64mb, 2583 files, ©undefined)
|
|
482
|
+
╭───────────────────────┬──────────────┬──────────┬───────┬───────────┬────────────┬───────────╮
|
|
483
|
+
│ Name │ Dependencies │ Size │ Files │ Native │ License │ Deprec │
|
|
484
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
485
|
+
│ @octokit/rest@19.0.3 │ 25 │ 11mb │ 380 │ │ MIT │ │
|
|
486
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
487
|
+
│ glob@10.3.5 │ 27 │ 3.08mb │ 367 │ │ ISC │ │
|
|
488
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
489
|
+
│ simple-git@3.22.0 │ 4 │ 934.57kb │ 142 │ │ MIT │ │
|
|
490
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
491
|
+
│ js-yaml@4.1.0 │ 1 │ 562.78kb │ 40 │ │ MIT │ │
|
|
492
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
493
|
+
│ xbytes@1.9.1 │ │ 74.45kb │ 11 │ │ Apache-2.0 │ │
|
|
494
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
495
|
+
│ tmp@0.2.3 │ │ 53.08kb │ 5 │ │ MIT │ │
|
|
496
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
497
|
+
│ debug@4.4.0 │ 1 │ 48.36kb │ 11 │ │ MIT │ │
|
|
498
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
499
|
+
│ deepmerge@4.3.1 │ │ 30.43kb │ 11 │ │ MIT │ │
|
|
500
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
501
|
+
│ require-dir@1.2.0 │ │ 16.87kb │ 40 │ │ MIT │ │
|
|
502
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
503
|
+
│ get-folder-size@5.0.0 │ │ 11.65kb │ 5 │ │ MIT │ │
|
|
504
|
+
├───────────────────────┼──────────────┼──────────┼───────┼───────────┼────────────┼───────────┤
|
|
505
|
+
│ pretty-bytes@5.6.0 │ │ 11.27kb │ 5 │ │ MIT │ │
|
|
506
|
+
╰───────────────────────┴──────────────┴──────────┴───────┴───────────┴────────────┴───────────╯
|
|
491
507
|
```
|
|
492
508
|
|
|
493
509
|
Configuration: supports `dir`
|
|
@@ -640,6 +656,23 @@ If you want to add a new automatic comparator for language X or dependency manag
|
|
|
640
656
|
6. test and validate
|
|
641
657
|
7. create PR
|
|
642
658
|
|
|
659
|
+
### How to release
|
|
660
|
+
|
|
661
|
+
How to create a new release of version `1.4` (as example):
|
|
662
|
+
|
|
663
|
+
1. Ensure all changes are merged to `main`
|
|
664
|
+
2. On `main` checkout, bump version in `package.json` to `1.4`
|
|
665
|
+
3. Run `npm publish --dry-run` to ensure all looks good
|
|
666
|
+
4. Commit straight to `main` like [here](https://github.com/adobe/sizewatcher/commit/4da848b6cc52a38f98203760f8f74e44a0ec77cd)
|
|
667
|
+
5. Create a new draft [release in Github](https://github.com/adobe/sizewatcher/releases)
|
|
668
|
+
1. Nmae the release `1.4`
|
|
669
|
+
2. Add release notes with new features, improvements, fixes
|
|
670
|
+
3. Ensure github creates a tag `v1.4`
|
|
671
|
+
6. Save draft and check things look good
|
|
672
|
+
7. Publish the release
|
|
673
|
+
8. The [Publish Package to NPM](https://github.com/adobe/sizewatcher/actions/workflows/npm-publish.yml) Github action workflow should automatically release the new version to npmjs.com
|
|
674
|
+
|
|
675
|
+
|
|
643
676
|
## Licensing
|
|
644
677
|
|
|
645
678
|
This project is licensed under the Apache V2 License. See [LICENSE](LICENSE) for more information.
|
package/lib/checkout.js
CHANGED
|
@@ -18,7 +18,9 @@ const debug = require("debug")("sizewatcher");
|
|
|
18
18
|
const tmp = require("tmp");
|
|
19
19
|
tmp.setGracefulCleanup();
|
|
20
20
|
const path = require("path");
|
|
21
|
+
// TODO: get rid of simple-git, replace with execSync('git ...') (sanitize user input!)
|
|
21
22
|
const Git = require("simple-git");
|
|
23
|
+
const { execSync } = require("child_process");
|
|
22
24
|
|
|
23
25
|
async function getGitRoot(dir) {
|
|
24
26
|
// --git-dir will return relative directories
|
|
@@ -36,10 +38,8 @@ async function currentBranch(dir) {
|
|
|
36
38
|
return branch;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
async function
|
|
40
|
-
|
|
41
|
-
const sha = await git.revparse(["HEAD"]);
|
|
42
|
-
return sha;
|
|
41
|
+
async function commitSha(dir, branch) {
|
|
42
|
+
return Git(dir).revparse([branch]);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
async function getDefaultBranch(dir) {
|
|
@@ -50,17 +50,18 @@ async function getDefaultBranch(dir) {
|
|
|
50
50
|
return m[1];
|
|
51
51
|
}
|
|
52
52
|
} catch (e) {
|
|
53
|
-
debug(
|
|
53
|
+
debug(`- ignoring error in getDefaultBranch(): ${e.message}`);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
// works with branch names and commit hashes
|
|
58
|
+
async function hasRef(dir, branch) {
|
|
58
59
|
try {
|
|
59
|
-
await Git(dir).
|
|
60
|
-
return
|
|
60
|
+
await Git(dir).catFile(["commit", branch]);
|
|
61
|
+
return true;
|
|
61
62
|
} catch (e) {
|
|
62
|
-
debug(
|
|
63
|
-
return
|
|
63
|
+
debug(`- git returned with non-zero exit code in hasRef(), treating as false: ${e.message}`);
|
|
64
|
+
return false;
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -73,9 +74,9 @@ async function getBeforeBranch(dir, branch) {
|
|
|
73
74
|
return branch
|
|
74
75
|
|| await getPullRequestBaseBranch()
|
|
75
76
|
|| await getDefaultBranch(dir)
|
|
76
|
-
|| await
|
|
77
|
-
|| await
|
|
78
|
-
|| await
|
|
77
|
+
|| (await hasRef(dir, "main") && "main")
|
|
78
|
+
|| (await hasRef(dir, "trunk") && "trunk")
|
|
79
|
+
|| (await hasRef(dir, "master") && "master"); // eslint-disable-line no-return-await
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
async function getAfterBranch(dir, branch) {
|
|
@@ -90,46 +91,47 @@ async function getAfterBranch(dir, branch) {
|
|
|
90
91
|
async function cloneRemoteAndCheckout(from, to, branch) {
|
|
91
92
|
// 1. get remote url from existing checkout from CI (in `from` dir)
|
|
92
93
|
const remoteUrl = (await Git(from).remote(["get-url", "origin"])).trim();
|
|
93
|
-
debug(
|
|
94
|
+
debug(`- git clone ${remoteUrl} into ${to}`);
|
|
94
95
|
|
|
95
96
|
// 2. git clone remote url into new directory `to`
|
|
96
97
|
await Git().clone(remoteUrl, to);
|
|
97
98
|
|
|
98
99
|
// 3. checkout desired branch
|
|
99
|
-
debug(
|
|
100
|
+
debug(`- git checkout '${branch}'...`);
|
|
100
101
|
await Git(to).checkout(branch);
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
async function cloneLocalAndCheckout(from, to, branch) {
|
|
104
105
|
const gitRoot = await getGitRoot(from);
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
// local clone will preserve the HEAD (aka same branch)
|
|
108
|
+
debug(`- local cloning ${gitRoot} into ${to}`);
|
|
107
109
|
await Git().clone(gitRoot, to, { '--local' : true });
|
|
108
110
|
|
|
109
111
|
if (branch) {
|
|
110
|
-
debug(
|
|
112
|
+
debug(`- checking out '${branch}'...`);
|
|
111
113
|
await Git(to).checkout(branch);
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
async function gitCheckoutBeforeAndAfter(gitDir, beforeBranch, afterBranch) {
|
|
116
|
-
|
|
117
|
-
beforeBranch
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
const resolvedBeforeBranch = await getBeforeBranch(gitDir, beforeBranch);
|
|
119
|
+
debug(`resolved before branch '${beforeBranch || ""}' => '${resolvedBeforeBranch}'`);
|
|
120
|
+
|
|
121
|
+
const resolvedAfterBranch = await getAfterBranch(gitDir, afterBranch);
|
|
122
|
+
debug(`resolved after branch '${afterBranch || ""}' => '${resolvedAfterBranch}'`);
|
|
123
|
+
|
|
124
|
+
if (resolvedBeforeBranch === resolvedAfterBranch) {
|
|
121
125
|
return {
|
|
122
126
|
before: {
|
|
123
|
-
branch:
|
|
127
|
+
branch: resolvedBeforeBranch
|
|
124
128
|
},
|
|
125
129
|
after: {
|
|
126
|
-
branch:
|
|
130
|
+
branch: resolvedAfterBranch,
|
|
127
131
|
}
|
|
128
132
|
};
|
|
129
133
|
}
|
|
130
134
|
|
|
131
|
-
debug(`cloning branches ${beforeBranch} (before) and ${afterBranch} (after)...`);
|
|
132
|
-
|
|
133
135
|
const tempDir = tmp.dirSync({unsafeCleanup: true}).name;
|
|
134
136
|
debug(`temporary directory: ${tempDir}`);
|
|
135
137
|
|
|
@@ -138,40 +140,53 @@ async function gitCheckoutBeforeAndAfter(gitDir, beforeBranch, afterBranch) {
|
|
|
138
140
|
const beforeDir = path.join(tempDir, "before");
|
|
139
141
|
|
|
140
142
|
// we can use the faster local clone only if the "branch" is present locally
|
|
141
|
-
if (await
|
|
142
|
-
debug(`[before] using local cloning because existing checkout already has
|
|
143
|
-
await cloneLocalAndCheckout(gitDir, beforeDir,
|
|
143
|
+
if (await hasRef(gitDir, resolvedBeforeBranch)) {
|
|
144
|
+
debug(`[before] using local cloning (because existing checkout already has '${resolvedBeforeBranch}')`);
|
|
145
|
+
await cloneLocalAndCheckout(gitDir, beforeDir, resolvedBeforeBranch);
|
|
144
146
|
} else {
|
|
145
|
-
debug(`[before] using remote cloning
|
|
147
|
+
debug(`[before] using remote cloning (because existing checkout does not have '${resolvedBeforeBranch}')`);
|
|
146
148
|
// ...otherwise need to do a full remote clone
|
|
147
|
-
await cloneRemoteAndCheckout(gitDir, beforeDir,
|
|
149
|
+
await cloneRemoteAndCheckout(gitDir, beforeDir, resolvedBeforeBranch);
|
|
148
150
|
}
|
|
149
151
|
|
|
152
|
+
debug(`[before] input branch : ${beforeBranch || ""}`);
|
|
153
|
+
debug(`[before] resolved branch: ${resolvedBeforeBranch}`);
|
|
154
|
+
const beforeSha = await commitSha(beforeDir, resolvedBeforeBranch);
|
|
155
|
+
debug(`[before] branch sha : ${beforeSha}`);
|
|
156
|
+
debug(`[before] actual ref : ${await currentBranch(beforeDir)}`);
|
|
157
|
+
|
|
158
|
+
// this is needed mostly for Github actions/checkout with its special PR merge commit replacing the actual last sha
|
|
159
|
+
debug(`[before] ensure full history - git fetch origin ${resolvedAfterBranch}:${resolvedAfterBranch}...`);
|
|
160
|
+
execSync(`git fetch origin ${resolvedAfterBranch}:${resolvedAfterBranch}`, { cwd: beforeDir });
|
|
161
|
+
|
|
150
162
|
// after checkout --------------------------------------------------------------
|
|
151
163
|
|
|
152
164
|
const afterDir = path.join(tempDir, "after");
|
|
153
165
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
166
|
+
// always local clone after which preserves the HEAD
|
|
167
|
+
// works well for CI checkouts where the checkout is in the after state already (unless afterBranch is specified)
|
|
168
|
+
// if user specified afterBranch on cli, we will checkout that branch after cloning
|
|
169
|
+
debug(`[after] local cloning${afterBranch ? ` and checking out '${afterBranch}'` : ""}`);
|
|
170
|
+
await cloneLocalAndCheckout(gitDir, afterDir, afterBranch);
|
|
171
|
+
|
|
172
|
+
debug(`[after] input branch : ${afterBranch || ""}`);
|
|
173
|
+
debug(`[after] resolved branch: ${resolvedAfterBranch}`);
|
|
174
|
+
const afterSha = await commitSha(beforeDir, resolvedAfterBranch);
|
|
175
|
+
debug(`[after] branch sha : ${afterSha}`);
|
|
176
|
+
debug(`[after] actual ref : ${await currentBranch(afterDir)}`);
|
|
163
177
|
|
|
164
178
|
debug("folders ready");
|
|
165
179
|
|
|
166
180
|
return {
|
|
167
181
|
before: {
|
|
168
182
|
dir: beforeDir,
|
|
169
|
-
branch:
|
|
183
|
+
branch: resolvedBeforeBranch,
|
|
184
|
+
sha: beforeSha
|
|
170
185
|
},
|
|
171
186
|
after: {
|
|
172
187
|
dir: afterDir,
|
|
173
|
-
branch:
|
|
174
|
-
sha:
|
|
188
|
+
branch: resolvedAfterBranch,
|
|
189
|
+
sha: afterSha
|
|
175
190
|
}
|
|
176
191
|
};
|
|
177
192
|
}
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
// comparators/custom.js - compares custom path
|
|
16
16
|
|
|
17
17
|
const debug = require("debug")("sizewatcher:custom");
|
|
18
|
-
const du = require("du");
|
|
19
18
|
const path = require("path");
|
|
20
19
|
const fs = require("fs");
|
|
21
20
|
const pb = require('pretty-bytes');
|
|
22
21
|
const { spawn } = require('child_process');
|
|
23
22
|
const glob = require("glob");
|
|
23
|
+
const { getFileSize } = require("../size");
|
|
24
24
|
|
|
25
25
|
async function spawnProcess(command, args, options) {
|
|
26
26
|
return new Promise((resolve, reject) => {
|
|
@@ -57,7 +57,7 @@ async function measure(baseDir, globPath, fileListing) {
|
|
|
57
57
|
const files = glob.sync(path.join(baseDir, globPath));
|
|
58
58
|
for (const file of files) {
|
|
59
59
|
if (fs.existsSync(file)) {
|
|
60
|
-
const fileSize = await
|
|
60
|
+
const fileSize = await getFileSize(file);
|
|
61
61
|
total += fileSize;
|
|
62
62
|
|
|
63
63
|
if (fileListing) {
|
package/lib/comparators/git.js
CHANGED
|
@@ -12,33 +12,54 @@
|
|
|
12
12
|
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
15
|
-
// comparators/git.js - compares git repo size
|
|
15
|
+
// comparators/git.js - compares git repo size
|
|
16
16
|
|
|
17
17
|
const debug = require("debug")("sizewatcher:git");
|
|
18
|
-
const du = require("du");
|
|
19
18
|
const path = require("path");
|
|
20
19
|
const fs = require("fs");
|
|
21
20
|
const { execSync } = require("child_process");
|
|
22
|
-
const
|
|
21
|
+
const { getFileSize, getFolderRegex } = require("../size");
|
|
23
22
|
|
|
24
23
|
async function getSize(dir) {
|
|
25
|
-
debug(`
|
|
26
|
-
await Git(dir).raw(["gc"]);
|
|
24
|
+
debug(`calculating folder size of ${dir}...`);
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
return getFileSize(dir, {
|
|
27
|
+
// do not include .git folder
|
|
28
|
+
ignore: getFolderRegex(path.join(dir, ".git"))
|
|
29
|
+
});
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
//
|
|
32
|
+
async function getLargestCommits(dir, beforeBranch, afterBranch) {
|
|
33
|
+
debug(`determining largest objects in commits between ${beforeBranch} and ${afterBranch}...`);
|
|
34
|
+
// 1. we then get the list of objects in the range
|
|
35
|
+
// 2. calculate the size of each object
|
|
36
|
+
// 3. sort the objects by size
|
|
37
|
+
// 4. format the output nicely (human readable KiB, MiB, GiB, etc)
|
|
36
38
|
return execSync(`git rev-list --objects ${beforeBranch}..${afterBranch} \
|
|
37
39
|
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
|
|
38
40
|
| sed -n 's/^blob //p' \
|
|
39
41
|
| sort -r --numeric-sort --key=2 \
|
|
40
|
-
| cut -c
|
|
41
|
-
| $(command -v gnumfmt || echo numfmt) --field=
|
|
42
|
+
| cut -c 41- \
|
|
43
|
+
| $(command -v gnumfmt || echo numfmt) --field=1 --to=iec-i --suffix=B --padding=7 --round=nearest`, { cwd: dir }).toString();
|
|
44
|
+
|
|
45
|
+
// Note: even if the command will fail this will not throw an error (because of the shell piping)
|
|
46
|
+
// this is ok because this is an informational output only
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function getLargestFiles(dir) {
|
|
50
|
+
debug(`determining largest objects in repo...`);
|
|
51
|
+
// 1. get the list of objects in the repo
|
|
52
|
+
// 2. sort the objects by size
|
|
53
|
+
// 3. show the top 10
|
|
54
|
+
// 3. format the output nicely (human readable KiB, MiB, GiB, etc)
|
|
55
|
+
return execSync(`git ls-tree -r --long HEAD \
|
|
56
|
+
| sort --key 4 -n -r \
|
|
57
|
+
| head -n 10 \
|
|
58
|
+
| cut -c 53- \
|
|
59
|
+
| $(command -v gnumfmt || echo numfmt) --field=1 --to=iec-i --suffix=B --padding=7 --round=nearest`, { cwd: dir }).toString();
|
|
60
|
+
|
|
61
|
+
// Note: even if the command will fail this will not throw an error (because of the shell piping)
|
|
62
|
+
// this is ok because this is an informational output only
|
|
42
63
|
}
|
|
43
64
|
|
|
44
65
|
module.exports = {
|
|
@@ -50,12 +71,19 @@ module.exports = {
|
|
|
50
71
|
compare: async function(before, after) {
|
|
51
72
|
const beforeSize = await getSize(before.dir);
|
|
52
73
|
const afterSize = await getSize(after.dir);
|
|
53
|
-
|
|
74
|
+
|
|
75
|
+
const largestFiles = await getLargestFiles(after.dir);
|
|
76
|
+
const largestCommits = await getLargestCommits(before.dir, before.branch, after.branch);
|
|
77
|
+
|
|
78
|
+
let details = `Largest files in repository checkout:\n\n${largestFiles}\n`;
|
|
79
|
+
if (largestCommits.length > 0) {
|
|
80
|
+
details += `Largest files among new changes:\n\n${largestCommits}`;
|
|
81
|
+
}
|
|
54
82
|
|
|
55
83
|
return {
|
|
56
84
|
beforeSize,
|
|
57
85
|
afterSize,
|
|
58
|
-
detailsLabel: "Largest files
|
|
86
|
+
detailsLabel: "Largest files",
|
|
59
87
|
details
|
|
60
88
|
};
|
|
61
89
|
}
|
|
@@ -16,9 +16,10 @@
|
|
|
16
16
|
|
|
17
17
|
const debug = require("debug")("sizewatcher:node_modules");
|
|
18
18
|
const { execSync } = require("child_process");
|
|
19
|
-
const du = require("du");
|
|
20
19
|
const path = require("path");
|
|
21
20
|
const fs = require("fs");
|
|
21
|
+
const { getFileSize } = require("../size");
|
|
22
|
+
|
|
22
23
|
|
|
23
24
|
function isEmptyObject(obj) {
|
|
24
25
|
return Object.keys(obj).length === 0 && obj.constructor === Object;
|
|
@@ -44,7 +45,7 @@ async function getSize(dir) {
|
|
|
44
45
|
const nodeModules = path.join(dir, "node_modules");
|
|
45
46
|
if (fs.existsSync(nodeModules)) {
|
|
46
47
|
debug(`calculating folder size of ${nodeModules}...`);
|
|
47
|
-
return
|
|
48
|
+
return getFileSize(nodeModules);
|
|
48
49
|
} else {
|
|
49
50
|
return 0;
|
|
50
51
|
}
|
|
@@ -62,16 +63,16 @@ async function costOfModules(dir) {
|
|
|
62
63
|
return "(no production dependencies)";
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
debug(`running
|
|
66
|
+
debug(`running npx howfat...`);
|
|
66
67
|
try {
|
|
67
|
-
const out = execSync("npx
|
|
68
|
+
const out = execSync("npx howfat -r table --sort size-").toString();
|
|
68
69
|
const PREFIX = "\nCalculating...\n\n\n";
|
|
69
70
|
if (out.startsWith(PREFIX)) {
|
|
70
71
|
return out.substring(PREFIX.length);
|
|
71
72
|
}
|
|
72
73
|
return out;
|
|
73
74
|
} catch (e) {
|
|
74
|
-
throw new Error(`
|
|
75
|
+
throw new Error(`howfat failed with: ${e.stdout ? e.stdout.toString() : ""} ${e.stdout ? e.stderr.toString() : ""}`);
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
}
|
package/lib/compare.js
CHANGED
|
@@ -48,10 +48,25 @@ async function compare(before, after) {
|
|
|
48
48
|
|
|
49
49
|
console.log("Calculating size changes for");
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
const comparators = [];
|
|
52
|
+
// ensure git comparator comes first as it needs a clean checkout (in after folder)
|
|
53
|
+
if (COMPARATORS.git) {
|
|
54
|
+
const comp = COMPARATORS.git;
|
|
55
|
+
comp.type = "git";
|
|
56
|
+
comparators.push(comp);
|
|
57
|
+
}
|
|
58
|
+
for (const type of Object.keys(COMPARATORS)) {
|
|
59
|
+
if (type !== "git") {
|
|
60
|
+
const comp = COMPARATORS[type];
|
|
61
|
+
comp.type = type;
|
|
62
|
+
comparators.push(comp);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const comparator of comparators) {
|
|
67
|
+
const type = comparator.type;
|
|
53
68
|
|
|
54
|
-
if (
|
|
69
|
+
if (type === "custom") {
|
|
55
70
|
// custom comparator, multiple could be configured
|
|
56
71
|
let customConfigs = config.comparators.custom;
|
|
57
72
|
if (!customConfigs) {
|
|
@@ -68,9 +83,9 @@ async function compare(before, after) {
|
|
|
68
83
|
|
|
69
84
|
} else {
|
|
70
85
|
// fixed comparators
|
|
71
|
-
const comparatorConfig = config.comparators[
|
|
86
|
+
const comparatorConfig = config.comparators[type];
|
|
72
87
|
if (comparatorConfig === false) {
|
|
73
|
-
debug(`skipping '${
|
|
88
|
+
debug(`skipping '${type}' because disabled in config`);
|
|
74
89
|
continue;
|
|
75
90
|
}
|
|
76
91
|
let dirs = ["."];
|
|
@@ -83,9 +98,9 @@ async function compare(before, after) {
|
|
|
83
98
|
}
|
|
84
99
|
for (const dir of dirs) {
|
|
85
100
|
if (dir !== ".") {
|
|
86
|
-
comparator.name = `${
|
|
101
|
+
comparator.name = `${type} [${dir}]`;
|
|
87
102
|
} else {
|
|
88
|
-
comparator.name =
|
|
103
|
+
comparator.name = type;
|
|
89
104
|
}
|
|
90
105
|
comparator.config = comparatorConfig || {};
|
|
91
106
|
|
|
@@ -103,7 +118,7 @@ async function compare(before, after) {
|
|
|
103
118
|
deltas.before = before;
|
|
104
119
|
deltas.after = after;
|
|
105
120
|
|
|
106
|
-
return
|
|
121
|
+
return processDeltas(deltas);
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
function exceeds(value, increasePct, limit) {
|
|
@@ -126,7 +141,7 @@ function exceeds(value, increasePct, limit) {
|
|
|
126
141
|
}
|
|
127
142
|
}
|
|
128
143
|
|
|
129
|
-
function
|
|
144
|
+
function processDeltas(deltas) {
|
|
130
145
|
const summary = {
|
|
131
146
|
cheers: 0,
|
|
132
147
|
ok: 0,
|
package/lib/config.js
CHANGED
|
@@ -39,7 +39,7 @@ function load() {
|
|
|
39
39
|
|
|
40
40
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
41
41
|
const data = fs.readFileSync(CONFIG_FILE, "utf8");
|
|
42
|
-
const loadedConfig = yaml.
|
|
42
|
+
const loadedConfig = yaml.load(data) || {};
|
|
43
43
|
|
|
44
44
|
config = deepmerge(DEFAULT_CONFIG, loadedConfig);
|
|
45
45
|
|
|
@@ -65,6 +65,6 @@ module.exports = {
|
|
|
65
65
|
},
|
|
66
66
|
|
|
67
67
|
asYaml() {
|
|
68
|
-
return yaml.
|
|
68
|
+
return yaml.dump(this.get());
|
|
69
69
|
}
|
|
70
70
|
};
|
package/lib/render.js
CHANGED
|
@@ -42,7 +42,7 @@ function indentEachLine(string, indent=" ") {
|
|
|
42
42
|
function renderAsText(deltas) {
|
|
43
43
|
let text = `Sizewatcher measured the following changes:\n\n`;
|
|
44
44
|
|
|
45
|
-
text += ` '${deltas.before.branch}' => '${deltas.after.branch}' (sha ${deltas.after.sha})\n\n`;
|
|
45
|
+
text += ` '${deltas.before.branch}' (sha ${deltas.before.sha}) => '${deltas.after.branch}' (sha ${deltas.after.sha})\n\n`;
|
|
46
46
|
|
|
47
47
|
for (const d of deltas) {
|
|
48
48
|
if (d.error) {
|
|
@@ -101,7 +101,7 @@ function renderAsMarkdown(deltas) {
|
|
|
101
101
|
|
|
102
102
|
markdown += `<details><summary>Notes</summary><br>\n\n`;
|
|
103
103
|
markdown += `- PR branch: \`${deltas.after.branch}\` @ ${deltas.after.sha}\n`;
|
|
104
|
-
markdown += `- Base branch: \`${deltas.before.branch}
|
|
104
|
+
markdown += `- Base branch: \`${deltas.before.branch}\` @ ${deltas.before.sha}\n`;
|
|
105
105
|
markdown += `- Sizewatcher v${require('../package.json').version}\n`;
|
|
106
106
|
markdown += `- Effective Configuration:\n`;
|
|
107
107
|
markdown += `\n\`\`\`yaml\n${config.asYaml()}\`\`\`\n`;
|
package/lib/size.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const debug = require("debug")("sizewatcher:size");
|
|
16
|
+
|
|
17
|
+
let _getFolderSize;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get the size of a folder or file
|
|
21
|
+
*
|
|
22
|
+
* @param {String} dir Path to the folder or file
|
|
23
|
+
* @param {Object} options Supported options:
|
|
24
|
+
* - ignore: Regex pattern. Any file or folder with a path that matches the pattern will not be counted in the total folder size.
|
|
25
|
+
* @returns {Promise<Number>} The size of the folder or file in bytes
|
|
26
|
+
*/
|
|
27
|
+
async function getFileSize(dir, options) {
|
|
28
|
+
// lazy load get-folder-size due to ESM vs. CommonJS
|
|
29
|
+
if (!_getFolderSize) {
|
|
30
|
+
_getFolderSize = (await import("get-folder-size")).default;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = await _getFolderSize(dir, options);
|
|
34
|
+
if (result.errors) {
|
|
35
|
+
debug(`get-folder-size of ${dir} returned some errors:`, result.errors);
|
|
36
|
+
}
|
|
37
|
+
return result.size;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function escapeRegex(string) {
|
|
41
|
+
return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get a regex pattern that matches a directory and all its descendants.
|
|
46
|
+
*
|
|
47
|
+
* @param {String} dir Path to the directory
|
|
48
|
+
* @returns {RegExp} The regex pattern
|
|
49
|
+
*/
|
|
50
|
+
function getFolderRegex(dir) {
|
|
51
|
+
return new RegExp(`^${escapeRegex(dir)}(\\/.*)?$`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get a regex pattern that matches a file exactly.
|
|
56
|
+
*
|
|
57
|
+
* @param {String} file Path to the file
|
|
58
|
+
* @returns {RegExp} The regex pattern
|
|
59
|
+
*/
|
|
60
|
+
function getFileRegex(file) {
|
|
61
|
+
return new RegExp(`^${escapeRegex(file)}$`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
getFileSize,
|
|
66
|
+
getFolderRegex,
|
|
67
|
+
getFileRegex
|
|
68
|
+
};
|
package/lib/sizewatcher.js
CHANGED
|
@@ -56,7 +56,7 @@ async function sizewatcher(argv) {
|
|
|
56
56
|
return process.exit();
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
console.log(`Comparing changes from '${before.branch}' to '${after.branch}'\n`);
|
|
59
|
+
console.log(`Comparing changes from '${before.branch}' (sha ${before.sha}) to '${after.branch}' (sha ${after.sha})\n`);
|
|
60
60
|
|
|
61
61
|
const deltas = await compare(before, after);
|
|
62
62
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/sizewatcher",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Warns if your pull requests introduce large size increases.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"sizewatcher": "
|
|
7
|
+
"sizewatcher": "index.js"
|
|
8
8
|
},
|
|
9
9
|
"author": "Adobe",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
|
-
"repository":
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/adobe/sizewatcher.git"
|
|
14
|
+
},
|
|
12
15
|
"keywords": [
|
|
13
16
|
"size",
|
|
14
17
|
"check",
|
|
@@ -19,31 +22,35 @@
|
|
|
19
22
|
"pull-request"
|
|
20
23
|
],
|
|
21
24
|
"scripts": {
|
|
22
|
-
"test": "nyc -r=text -r=lcov mocha",
|
|
25
|
+
"test": "DEBUG=sizewatcher* nyc -r=text -r=lcov mocha",
|
|
23
26
|
"posttest": "eslint .",
|
|
24
27
|
"report-coverage": "nyc report --reporter=text-lcov | coveralls",
|
|
25
|
-
"sizewatcher": "DEBUG=sizewatcher* node ./index.js"
|
|
28
|
+
"sizewatcher": "DEBUG=sizewatcher* node ./index.js",
|
|
29
|
+
"clean": "rm -rf test/checkout/*/build"
|
|
26
30
|
},
|
|
27
31
|
"devDependencies": {
|
|
28
32
|
"@adobe/eslint-config-asset-compute": "^1.3.0",
|
|
29
33
|
"coveralls": "^3.1.0",
|
|
30
|
-
"eslint": "^
|
|
31
|
-
"mocha": "^
|
|
32
|
-
"mock-fs": "^
|
|
33
|
-
"nyc": "^
|
|
34
|
+
"eslint": "^8.0.0",
|
|
35
|
+
"mocha": "^11.0.0",
|
|
36
|
+
"mock-fs": "^5.5.0",
|
|
37
|
+
"nyc": "^17.0.0",
|
|
34
38
|
"supports-color": "^8.1.1"
|
|
35
39
|
},
|
|
36
40
|
"dependencies": {
|
|
37
|
-
"@octokit/rest": "
|
|
38
|
-
"debug": "
|
|
39
|
-
"deepmerge": "
|
|
40
|
-
"
|
|
41
|
-
"glob": "
|
|
42
|
-
"js-yaml": "
|
|
43
|
-
"pretty-bytes": "
|
|
44
|
-
"require-dir": "
|
|
45
|
-
"simple-git": "
|
|
46
|
-
"tmp": "
|
|
47
|
-
"xbytes": "
|
|
41
|
+
"@octokit/rest": "19.0.3",
|
|
42
|
+
"debug": "4.4.0",
|
|
43
|
+
"deepmerge": "4.3.1",
|
|
44
|
+
"get-folder-size": "5.0.0",
|
|
45
|
+
"glob": "10.3.5",
|
|
46
|
+
"js-yaml": "4.1.0",
|
|
47
|
+
"pretty-bytes": "5.6.0",
|
|
48
|
+
"require-dir": "1.2.0",
|
|
49
|
+
"simple-git": "3.22.0",
|
|
50
|
+
"tmp": "0.2.3",
|
|
51
|
+
"xbytes": "1.9.1"
|
|
52
|
+
},
|
|
53
|
+
"overrides": {
|
|
54
|
+
"whatwg-url": "^14.0.0"
|
|
48
55
|
}
|
|
49
56
|
}
|