registry-sync 7.1.0 → 8.1.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 +2 -2
- package/bin/sync +1 -2
- package/package.json +15 -25
- package/src/client.js +10 -13
- package/src/download.js +22 -22
- package/src/index.js +9 -11
- package/src/integrity.js +4 -9
- package/src/metadata.js +15 -21
- package/src/normalize-yarn-pattern.js +1 -4
- package/src/pregyp.js +15 -14
- package/src/resolve.js +17 -19
- package/src/sync.js +7 -10
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ The local copy can then be used as a simple private NPM registry without publish
|
|
|
8
8
|
|
|
9
9
|
## Pre-requisites
|
|
10
10
|
|
|
11
|
-
- Node.js
|
|
11
|
+
- Node.js v22.18.0 or newer
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
@@ -118,5 +118,5 @@ See [releases](https://github.com/heikkipora/registry-sync/releases).
|
|
|
118
118
|
|
|
119
119
|
## Contributing
|
|
120
120
|
|
|
121
|
-
Pull requests are welcome. Kindly check that your code passes ESLint checks by running `npm run eslint
|
|
121
|
+
Pull requests are welcome. Kindly check that your code passes ESLint checks by running `npm run eslint` first.
|
|
122
122
|
Integration tests can be run with `npm test`. Both are anyway run automatically by GitHub Actions.
|
package/bin/sync
CHANGED
package/package.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "registry-sync",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "synchronize a remote npm registry for private use",
|
|
5
5
|
"repository": "https://github.com/heikkipora/registry-sync",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"bin": {
|
|
7
8
|
"registry-sync": "bin/sync"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"build": "./build-npm",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"lint-staged": "lint-staged --verbose",
|
|
16
|
-
"test": "mocha -r ts-node/register --config test/.mocharc.js --timeout 120000 test/*.ts",
|
|
12
|
+
"eslint": "eslint --max-warnings=0 src test release-test/server/src",
|
|
13
|
+
"fix-eslint": "eslint --fix src test release-test/server/src",
|
|
14
|
+
"test": "mocha --timeout 120000 test/*.ts",
|
|
15
|
+
"typecheck": "tsc",
|
|
17
16
|
"release-test": "cd release-test && ./run-sync-install-cycle.sh"
|
|
18
17
|
},
|
|
19
18
|
"author": "Heikki Pora",
|
|
@@ -22,37 +21,28 @@
|
|
|
22
21
|
"@yarnpkg/lockfile": "1.1.0",
|
|
23
22
|
"axios": "1.13.2",
|
|
24
23
|
"commander": "14.0.2",
|
|
25
|
-
"lru-cache": "11.2.
|
|
24
|
+
"lru-cache": "11.2.4",
|
|
26
25
|
"semver": "7.7.3",
|
|
27
26
|
"ssri": "13.0.0",
|
|
28
27
|
"tar-fs": "3.1.1"
|
|
29
28
|
},
|
|
30
29
|
"devDependencies": {
|
|
31
|
-
"@arkweid/lefthook": "0.7.7",
|
|
32
|
-
"@eslint/eslintrc": "3.3.1",
|
|
33
|
-
"@eslint/js": "9.39.1",
|
|
34
30
|
"@types/chai": "5.2.3",
|
|
35
|
-
"@types/lodash": "4.17.
|
|
31
|
+
"@types/lodash": "4.17.21",
|
|
36
32
|
"@types/mocha": "10.0.10",
|
|
37
33
|
"@types/node": "20.17.32",
|
|
38
34
|
"@types/semver": "7.7.1",
|
|
39
35
|
"@types/ssri": "7.1.5",
|
|
40
36
|
"@types/tar-fs": "2.0.4",
|
|
41
37
|
"@types/yarnpkg__lockfile": "1.1.9",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"chai": "6.2.1",
|
|
45
|
-
"eslint": "9.39.1",
|
|
46
|
-
"eslint-config-prettier": "10.1.8",
|
|
47
|
-
"eslint-formatter-codeframe": "7.32.2",
|
|
38
|
+
"chai": "6.2.2",
|
|
39
|
+
"eslint": "9.39.2",
|
|
48
40
|
"eslint-plugin-mocha": "11.2.0",
|
|
49
|
-
"express": "5.1
|
|
50
|
-
"globals": "
|
|
51
|
-
"lint-staged": "16.2.6",
|
|
41
|
+
"express": "5.2.1",
|
|
42
|
+
"globals": "17.0.0",
|
|
52
43
|
"mocha": "11.7.5",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"typescript": "5.9.3"
|
|
44
|
+
"typescript": "5.9.3",
|
|
45
|
+
"typescript-eslint": "8.51.0"
|
|
56
46
|
},
|
|
57
47
|
"keywords": [
|
|
58
48
|
"registry",
|
|
@@ -62,6 +52,6 @@
|
|
|
62
52
|
"offline"
|
|
63
53
|
],
|
|
64
54
|
"engines": {
|
|
65
|
-
"node": ">=
|
|
55
|
+
"node": ">=22.18.0"
|
|
66
56
|
}
|
|
67
57
|
}
|
package/src/client.js
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const axios_1 = require("axios");
|
|
7
|
-
const lru_cache_1 = require("lru-cache");
|
|
8
|
-
const metadataCache = new lru_cache_1.LRUCache({ max: 100 });
|
|
9
|
-
const client = axios_1.default.create({
|
|
1
|
+
import * as https from 'https';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { LRUCache } from 'lru-cache';
|
|
4
|
+
const metadataCache = new LRUCache({ max: 100 });
|
|
5
|
+
const client = axios.create({
|
|
10
6
|
httpsAgent: new https.Agent({ keepAlive: true }),
|
|
11
7
|
timeout: 30 * 1000
|
|
12
8
|
});
|
|
13
|
-
async function fetchJsonWithCacheCloned(url, token) {
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
export async function fetchJsonWithCacheCloned(url, token) {
|
|
10
|
+
const cached = metadataCache.get(url);
|
|
11
|
+
if (cached) {
|
|
12
|
+
return structuredClone(cached);
|
|
16
13
|
}
|
|
17
14
|
const value = await fetch(url, 'json', token);
|
|
18
15
|
metadataCache.set(url, value);
|
|
19
16
|
return structuredClone(value);
|
|
20
17
|
}
|
|
21
|
-
function fetchBinaryData(url, token) {
|
|
18
|
+
export function fetchBinaryData(url, token) {
|
|
22
19
|
return fetch(url, 'arraybuffer', token);
|
|
23
20
|
}
|
|
24
21
|
async function fetch(url, responseType, token) {
|
package/src/download.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const metadata_1 = require("./metadata");
|
|
12
|
-
async function downloadAll(packages, { localUrl, prebuiltBinaryProperties, registryUrl, registryToken, rootFolder, enforceTarballsOverHttps }) {
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as semver from 'semver';
|
|
4
|
+
import * as url from 'url';
|
|
5
|
+
import assert from 'assert';
|
|
6
|
+
import { downloadPrebuiltBinaries, hasPrebuiltBinaries } from "./pregyp.js";
|
|
7
|
+
import { fetchBinaryData, fetchJsonWithCacheCloned } from "./client.js";
|
|
8
|
+
import { rewriteMetadataInTarball, rewriteVersionMetadata, tarballFilename } from "./metadata.js";
|
|
9
|
+
import { verifyIntegrity } from "./integrity.js";
|
|
10
|
+
export async function downloadAll(packages, { localUrl, prebuiltBinaryProperties, registryUrl, registryToken, rootFolder, enforceTarballsOverHttps }) {
|
|
13
11
|
const downloadFromRegistry = download.bind(null, registryUrl, registryToken, localUrl, rootFolder, prebuiltBinaryProperties, enforceTarballsOverHttps);
|
|
14
12
|
for (const pkg of packages) {
|
|
15
13
|
await downloadFromRegistry(pkg);
|
|
@@ -23,19 +21,19 @@ async function download(registryUrl, registryToken, localUrl, rootFolder, prebui
|
|
|
23
21
|
}
|
|
24
22
|
const localFolder = await ensureLocalFolderExists(name, rootFolder);
|
|
25
23
|
let data = await downloadTarball(versionMetadata, enforceTarballsOverHttps, registryToken);
|
|
26
|
-
if (
|
|
24
|
+
if (hasPrebuiltBinaries(versionMetadata)) {
|
|
27
25
|
const localPregypFolder = await ensureLocalFolderExists(version, localFolder);
|
|
28
|
-
await
|
|
29
|
-
data = await
|
|
26
|
+
await downloadPrebuiltBinaries(versionMetadata, localPregypFolder, prebuiltBinaryProperties);
|
|
27
|
+
data = await rewriteMetadataInTarball(data, versionMetadata, localUrl, localFolder);
|
|
30
28
|
}
|
|
31
29
|
await saveTarball(versionMetadata, data, localFolder);
|
|
32
|
-
|
|
30
|
+
rewriteVersionMetadata(versionMetadata, data, localUrl);
|
|
33
31
|
await updateMetadata(versionMetadata, registryMetadata, registryUrl, localFolder);
|
|
34
32
|
}
|
|
35
33
|
async function downloadTarball({ _id: id, dist }, enforceTarballsOverHttps, registryToken) {
|
|
36
34
|
const tarballUrl = enforceTarballsOverHttps ? dist.tarball.replace('http://', 'https://') : dist.tarball;
|
|
37
|
-
const data = await
|
|
38
|
-
|
|
35
|
+
const data = await fetchBinaryData(tarballUrl, registryToken);
|
|
36
|
+
verifyIntegrity(data, id, dist);
|
|
39
37
|
return data;
|
|
40
38
|
}
|
|
41
39
|
function saveTarball({ name, version }, data, localFolder) {
|
|
@@ -50,16 +48,18 @@ async function updateMetadata(versionMetadata, defaultMetadata, registryUrl, loc
|
|
|
50
48
|
localMetadata['dist-tags'] = collectDistTags(localMetadata, defaultMetadata);
|
|
51
49
|
await saveMetadata(localMetadataPath, localMetadata);
|
|
52
50
|
}
|
|
53
|
-
// Collect
|
|
51
|
+
// Collect dist-tags entries (name -> version) from registry metadata,
|
|
54
52
|
// which point to versions we have locally available.
|
|
55
53
|
// Override 'latest' tag to ensure its validity as we might not have the version
|
|
56
54
|
// that is tagged latest in registry
|
|
57
55
|
function collectDistTags(localMetadata, defaultMetadata) {
|
|
58
56
|
const availableVersions = Object.keys(localMetadata.versions);
|
|
59
57
|
const validDistTags = Object.entries(defaultMetadata['dist-tags']).filter(([, version]) => availableVersions.includes(version));
|
|
58
|
+
const latest = availableVersions.sort(semver.compare).pop();
|
|
59
|
+
assert(latest, 'At least one version should be locally available to determine "latest" dist-tag');
|
|
60
60
|
return {
|
|
61
61
|
...Object.fromEntries(validDistTags),
|
|
62
|
-
latest
|
|
62
|
+
latest
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
async function loadMetadata(path, defaultMetadata) {
|
|
@@ -76,7 +76,7 @@ function saveMetadata(path, metadata) {
|
|
|
76
76
|
return fs.promises.writeFile(path, json, 'utf8');
|
|
77
77
|
}
|
|
78
78
|
function tarballPath(name, version, localFolder) {
|
|
79
|
-
return path.join(localFolder,
|
|
79
|
+
return path.join(localFolder, tarballFilename(name, version));
|
|
80
80
|
}
|
|
81
81
|
async function ensureLocalFolderExists(name, rootFolder) {
|
|
82
82
|
const localFolder = path.resolve(rootFolder, name);
|
|
@@ -85,5 +85,5 @@ async function ensureLocalFolderExists(name, rootFolder) {
|
|
|
85
85
|
}
|
|
86
86
|
function fetchMetadataCloned(name, registryUrl, registryToken) {
|
|
87
87
|
const urlSafeName = name.replace(/\//g, '%2f');
|
|
88
|
-
return
|
|
88
|
+
return fetchJsonWithCacheCloned(url.resolve(registryUrl, urlSafeName), registryToken);
|
|
89
89
|
}
|
package/src/index.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const { version } = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
9
|
-
const program = new commander_1.Command();
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { synchronize } from "./sync.js";
|
|
5
|
+
import { URL } from 'url';
|
|
6
|
+
const { version } = JSON.parse(fs.readFileSync(path.join(import.meta.dirname, '..', 'package.json'), 'utf-8'));
|
|
7
|
+
const program = new Command();
|
|
10
8
|
program
|
|
11
9
|
.version(version)
|
|
12
10
|
.requiredOption('--root <path>', 'Path to save NPM package tarballs and metadata to')
|
|
@@ -35,7 +33,7 @@ const prebuiltBinaryProperties = abis
|
|
|
35
33
|
.map(abi => architectures.map(arch => platforms.map(platform => ({ abi, arch, platform }))).flat())
|
|
36
34
|
.flat();
|
|
37
35
|
const options = {
|
|
38
|
-
localUrl: new
|
|
36
|
+
localUrl: new URL(rawOptions.localUrl),
|
|
39
37
|
manifest: rawOptions.manifest,
|
|
40
38
|
prebuiltBinaryProperties,
|
|
41
39
|
registryUrl: rawOptions.registryUrl || 'https://registry.npmjs.org',
|
|
@@ -45,4 +43,4 @@ const options = {
|
|
|
45
43
|
includeDevDependencies: Boolean(rawOptions.includeDev),
|
|
46
44
|
dryRun: Boolean(rawOptions.dryRun)
|
|
47
45
|
};
|
|
48
|
-
|
|
46
|
+
synchronize(options);
|
package/src/integrity.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.verifyIntegrity = verifyIntegrity;
|
|
4
|
-
exports.sha1 = sha1;
|
|
5
|
-
exports.sha512 = sha512;
|
|
6
|
-
const ssri = require("ssri");
|
|
7
|
-
function verifyIntegrity(data, id, { integrity, shasum }) {
|
|
1
|
+
import * as ssri from 'ssri';
|
|
2
|
+
export function verifyIntegrity(data, id, { integrity, shasum }) {
|
|
8
3
|
if (!integrity && !shasum) {
|
|
9
4
|
throw new Error(`Integrity values not present in metadata for ${id}`);
|
|
10
5
|
}
|
|
@@ -17,9 +12,9 @@ function verifyIntegrity(data, id, { integrity, shasum }) {
|
|
|
17
12
|
throw new Error(`Integrity check with SHA1 failed for failed for ${id}`);
|
|
18
13
|
}
|
|
19
14
|
}
|
|
20
|
-
function sha1(data) {
|
|
15
|
+
export function sha1(data) {
|
|
21
16
|
return ssri.fromData(data, { algorithms: ['sha1'] }).hexDigest();
|
|
22
17
|
}
|
|
23
|
-
function sha512(data) {
|
|
18
|
+
export function sha512(data) {
|
|
24
19
|
return ssri.fromData(data, { algorithms: ['sha512'] }).toString();
|
|
25
20
|
}
|
package/src/metadata.js
CHANGED
|
@@ -1,26 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const tar = require("tar-fs");
|
|
10
|
-
const zlib = require("zlib");
|
|
11
|
-
const pregyp_1 = require("./pregyp");
|
|
12
|
-
const stream_1 = require("stream");
|
|
13
|
-
const integrity_1 = require("./integrity");
|
|
14
|
-
function rewriteVersionMetadata(versionMetadata, data, localUrl) {
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as tar from 'tar-fs';
|
|
4
|
+
import * as zlib from 'zlib';
|
|
5
|
+
import { hasPrebuiltBinaries } from "./pregyp.js";
|
|
6
|
+
import { Readable } from 'stream';
|
|
7
|
+
import { sha1, sha512 } from "./integrity.js";
|
|
8
|
+
export function rewriteVersionMetadata(versionMetadata, data, localUrl) {
|
|
15
9
|
versionMetadata.dist.tarball = localTarballUrl(versionMetadata, localUrl);
|
|
16
|
-
if (
|
|
10
|
+
if (hasPrebuiltBinaries(versionMetadata)) {
|
|
17
11
|
versionMetadata.binary.host = localUrl.origin;
|
|
18
12
|
versionMetadata.binary.remote_path = createPrebuiltBinaryRemotePath(localUrl, versionMetadata);
|
|
19
|
-
versionMetadata.dist.integrity =
|
|
20
|
-
versionMetadata.dist.shasum =
|
|
13
|
+
versionMetadata.dist.integrity = sha512(data);
|
|
14
|
+
versionMetadata.dist.shasum = sha1(data);
|
|
21
15
|
}
|
|
22
16
|
}
|
|
23
|
-
async function rewriteMetadataInTarball(data, versionMetadata, localUrl, localFolder) {
|
|
17
|
+
export async function rewriteMetadataInTarball(data, versionMetadata, localUrl, localFolder) {
|
|
24
18
|
const tmpFolder = path.join(localFolder, '.tmp');
|
|
25
19
|
await fs.promises.mkdir(tmpFolder, { recursive: true });
|
|
26
20
|
await extractTgz(data, tmpFolder);
|
|
@@ -37,9 +31,9 @@ async function rewriteMetadataInTarball(data, versionMetadata, localUrl, localFo
|
|
|
37
31
|
function createPrebuiltBinaryRemotePath(url, versionMetadata) {
|
|
38
32
|
return `${removeTrailingSlash(url.pathname)}/${versionMetadata.name}/${versionMetadata.version}/`;
|
|
39
33
|
}
|
|
40
|
-
function extractTgz(data, folder) {
|
|
34
|
+
export function extractTgz(data, folder) {
|
|
41
35
|
return new Promise((resolve, reject) => {
|
|
42
|
-
const tgz =
|
|
36
|
+
const tgz = Readable.from(data).pipe(zlib.createGunzip()).pipe(tar.extract(folder));
|
|
43
37
|
tgz.on('finish', resolve);
|
|
44
38
|
tgz.on('error', reject);
|
|
45
39
|
});
|
|
@@ -56,7 +50,7 @@ function compressTgz(folder) {
|
|
|
56
50
|
function localTarballUrl({ name, version }, localUrl) {
|
|
57
51
|
return `${localUrl.origin}${removeTrailingSlash(localUrl.pathname)}/${name}/${tarballFilename(name, version)}`;
|
|
58
52
|
}
|
|
59
|
-
function tarballFilename(name, version) {
|
|
53
|
+
export function tarballFilename(name, version) {
|
|
60
54
|
const normalized = name.replace(/\//g, '-');
|
|
61
55
|
return `${normalized}-${version}.tgz`;
|
|
62
56
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/*
|
|
3
2
|
BSD 2-Clause License
|
|
4
3
|
|
|
@@ -28,9 +27,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
28
27
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
28
|
*/
|
|
30
29
|
// From https://github.com/yarnpkg/yarn/blob/953c8b6a20e360b097625d64189e6e56ed813e0f/src/util/normalize-pattern.js#L2
|
|
31
|
-
|
|
32
|
-
exports.normalizeYarnPackagePattern = normalizeYarnPackagePattern;
|
|
33
|
-
function normalizeYarnPackagePattern(pattern) {
|
|
30
|
+
export function normalizeYarnPackagePattern(pattern) {
|
|
34
31
|
let hasVersion = false;
|
|
35
32
|
let range = 'latest';
|
|
36
33
|
let name = pattern;
|
package/src/pregyp.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as semver from 'semver';
|
|
4
|
+
import * as url from 'url';
|
|
5
|
+
import { fetchBinaryData } from "./client.js";
|
|
6
|
+
export function hasPrebuiltBinaries(metadata) {
|
|
7
|
+
return Boolean(metadata.binary
|
|
8
|
+
&& metadata.binary.host
|
|
9
|
+
&& metadata.binary.module_name
|
|
10
|
+
&& metadata.binary.package_name
|
|
11
|
+
&& metadata.binary.remote_path);
|
|
12
12
|
}
|
|
13
|
-
async function downloadPrebuiltBinaries(versionMetadata, localFolder, prebuiltBinaryProperties) {
|
|
13
|
+
export async function downloadPrebuiltBinaries(versionMetadata, localFolder, prebuiltBinaryProperties) {
|
|
14
14
|
const { binary, name, version } = versionMetadata;
|
|
15
15
|
if (!binary.napi_versions) {
|
|
16
16
|
for (const { abi, arch, platform } of prebuiltBinaryProperties) {
|
|
@@ -31,6 +31,7 @@ async function downloadPrebuiltBinary(localFolder, name, version, binary, abi, p
|
|
|
31
31
|
}
|
|
32
32
|
catch (err) {
|
|
33
33
|
// pre-built binaries are commonly not available on all platforms (and S3 will commonly respond with 403 for a non-existent file)
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
35
|
const fileNotFoundError = err.response && (err.response.status == 403 || err.response.status == 404);
|
|
35
36
|
if (!fileNotFoundError) {
|
|
36
37
|
console.error(`Unexpected error fetching prebuilt binary for ${name} and ABI v${abi} on ${arch}-${platform} (n-api version ${napiVersion})`);
|
|
@@ -39,7 +40,7 @@ async function downloadPrebuiltBinary(localFolder, name, version, binary, abi, p
|
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
function fetchPrebuiltBinary(name, version, binary, abi, platform, arch, napiVersion) {
|
|
42
|
-
return
|
|
43
|
+
return fetchBinaryData(prebuiltBinaryUrl(name, version, binary, abi, platform, arch, napiVersion), '');
|
|
43
44
|
}
|
|
44
45
|
function prebuiltBinaryFilePath(localFolder, name, version, binary, abi, platform, arch, napiVersion) {
|
|
45
46
|
return path.join(localFolder, prebuiltBinaryFileName(name, version, binary, abi, platform, arch, napiVersion));
|
|
@@ -57,7 +58,7 @@ function prebuiltBinaryFileName(name, version, binary, abi, platform, arch, napi
|
|
|
57
58
|
}
|
|
58
59
|
// see node-pre-gyp: /lib/util/versioning.js for documentation of possible values
|
|
59
60
|
function formatPrebuilt(formatString, name, version, moduleName, abi, platform, arch, napiVersion) {
|
|
60
|
-
const moduleVersion = semver.parse(version);
|
|
61
|
+
const moduleVersion = semver.parse(version, false, true);
|
|
61
62
|
const prerelease = (moduleVersion.prerelease || []).join('.');
|
|
62
63
|
const build = (moduleVersion.build || []).join('.');
|
|
63
64
|
const formatted = formatString
|
package/src/resolve.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const readline = require("readline");
|
|
9
|
-
const url = require("url");
|
|
10
|
-
const assert_1 = require("assert");
|
|
11
|
-
const yarnLockfile = require("@yarnpkg/lockfile");
|
|
12
|
-
const normalize_yarn_pattern_1 = require("./normalize-yarn-pattern");
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as pathLib from 'path';
|
|
3
|
+
import * as readline from 'readline';
|
|
4
|
+
import * as url from 'url';
|
|
5
|
+
import assert, { deepStrictEqual } from 'assert';
|
|
6
|
+
import yarnLockfile from '@yarnpkg/lockfile';
|
|
7
|
+
import { normalizeYarnPackagePattern } from "./normalize-yarn-pattern.js";
|
|
13
8
|
const YARN_LOCK_FILENAME = 'yarn.lock';
|
|
14
|
-
async function updateDependenciesCache(newDependencies, cacheFilePath, prebuiltBinaryProperties) {
|
|
9
|
+
export async function updateDependenciesCache(newDependencies, cacheFilePath, prebuiltBinaryProperties) {
|
|
15
10
|
const { dependencies: cachedDependencies } = await loadCache(cacheFilePath);
|
|
16
11
|
const dependencies = cachedDependencies.concat(newDependencies).sort(sortById).filter(uniqueById);
|
|
17
12
|
const data = {
|
|
@@ -21,7 +16,7 @@ async function updateDependenciesCache(newDependencies, cacheFilePath, prebuiltB
|
|
|
21
16
|
};
|
|
22
17
|
return fs.promises.writeFile(cacheFilePath, JSON.stringify(data), 'utf8');
|
|
23
18
|
}
|
|
24
|
-
async function dependenciesNotInCache(dependencies, cacheFilePath, prebuiltBinaryProperties) {
|
|
19
|
+
export async function dependenciesNotInCache(dependencies, cacheFilePath, prebuiltBinaryProperties) {
|
|
25
20
|
const { dependencies: cachedDependencies, prebuiltBinaryProperties: cachedPrebuiltBinaryProperties, prebuiltBinaryNApiSupport } = await loadCache(cacheFilePath);
|
|
26
21
|
if (cachedDependencies.length > 0 &&
|
|
27
22
|
(!isDeepEqual(prebuiltBinaryProperties, cachedPrebuiltBinaryProperties) || !prebuiltBinaryNApiSupport)) {
|
|
@@ -85,6 +80,7 @@ function isNonRegistryYarnPackagePattern(packagePattern) {
|
|
|
85
80
|
return path.split('/').filter((p) => !!p).length === 2;
|
|
86
81
|
}
|
|
87
82
|
}
|
|
83
|
+
return false;
|
|
88
84
|
}
|
|
89
85
|
function resolvePackageNameFromRegistryYarnPackagePattern(packagePattern) {
|
|
90
86
|
// See https://github.com/yarnpkg/yarn/blob/953c8b6a20e360b097625d64189e6e56ed813e0f/src/resolvers/exotics/registry-resolver.js#L12
|
|
@@ -108,7 +104,7 @@ function resolveNpmPackagesFromYarnLockDependencies(yarnLockDependencies) {
|
|
|
108
104
|
else {
|
|
109
105
|
// Package pattern not yet recognized, continue with parsing logic from
|
|
110
106
|
// https://github.com/yarnpkg/yarn/blob/953c8b6a20e360b097625d64189e6e56ed813e0f/src/package-request.js#L99
|
|
111
|
-
const { name: namePart, range: rangePart } =
|
|
107
|
+
const { name: namePart, range: rangePart } = normalizeYarnPackagePattern(packagePattern);
|
|
112
108
|
if (isNonRegistryYarnPackagePattern(rangePart)) {
|
|
113
109
|
return filterMappedDependencies;
|
|
114
110
|
}
|
|
@@ -129,7 +125,7 @@ function resolveNpmPackagesFromYarnLockDependencies(yarnLockDependencies) {
|
|
|
129
125
|
async function parseDependenciesFromNpmLockFile(lockFilepath, includeDevDependencies) {
|
|
130
126
|
const packageLock = JSON.parse(await fs.promises.readFile(lockFilepath, 'utf8'));
|
|
131
127
|
const fileVersion = packageLock.lockfileVersion || 1;
|
|
132
|
-
if (![2, 3].includes(
|
|
128
|
+
if (![2, 3].includes(fileVersion)) {
|
|
133
129
|
throw new Error(`Unsupported package-lock.json version ${fileVersion}`);
|
|
134
130
|
}
|
|
135
131
|
const dependencies = collectNpmLockfileDependencies(packageLock, includeDevDependencies);
|
|
@@ -162,7 +158,7 @@ async function parseDependenciesFromYarnLockFile(lockFilepath) {
|
|
|
162
158
|
const yarnLockDependencies = Object.entries(packagePatternToLockedVersion).map(([packagePattern, { version }]) => ({ packagePattern, version }));
|
|
163
159
|
return resolveNpmPackagesFromYarnLockDependencies(yarnLockDependencies);
|
|
164
160
|
}
|
|
165
|
-
async function dependenciesFromPackageLock(path, includeDevDependencies) {
|
|
161
|
+
export async function dependenciesFromPackageLock(path, includeDevDependencies) {
|
|
166
162
|
const filename = pathLib.basename(path);
|
|
167
163
|
const dependencies = filename === YARN_LOCK_FILENAME
|
|
168
164
|
? await parseDependenciesFromYarnLockFile(path)
|
|
@@ -194,11 +190,13 @@ function collectNpmLockfileDependencies({ packages }, includeDevDependencies) {
|
|
|
194
190
|
// "node_modules/lodash" -> "lodash"
|
|
195
191
|
// "node_modules/make-dir/node_modules/semver" -> "semver"
|
|
196
192
|
function pathToName(path) {
|
|
197
|
-
|
|
193
|
+
const name = path.split('node_modules/').pop();
|
|
194
|
+
assert(name, `Failed to extract package name from path ${path}`);
|
|
195
|
+
return name;
|
|
198
196
|
}
|
|
199
197
|
function isDeepEqual(a, b) {
|
|
200
198
|
try {
|
|
201
|
-
|
|
199
|
+
deepStrictEqual(a, b);
|
|
202
200
|
return true;
|
|
203
201
|
}
|
|
204
202
|
catch {
|
package/src/sync.js
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const download_1 = require("./download");
|
|
5
|
-
const resolve_1 = require("./resolve");
|
|
6
|
-
async function synchronize(options) {
|
|
1
|
+
import { dependenciesFromPackageLock, dependenciesNotInCache, updateDependenciesCache } from "./resolve.js";
|
|
2
|
+
import { downloadAll } from "./download.js";
|
|
3
|
+
export async function synchronize(options) {
|
|
7
4
|
const cacheFilePath = `${options.rootFolder}/.registry-sync-cache.json`;
|
|
8
|
-
const packages = await
|
|
9
|
-
const newPackages = await
|
|
5
|
+
const packages = await dependenciesFromPackageLock(options.manifest, options.includeDevDependencies);
|
|
6
|
+
const newPackages = await dependenciesNotInCache(packages, cacheFilePath, options.prebuiltBinaryProperties);
|
|
10
7
|
if (options.dryRun) {
|
|
11
8
|
console.log(newPackages.map(({ name, version }) => `${name}@${version}`).join('\n'));
|
|
12
9
|
console.log(`\nWould download ${newPackages.length} packages.`);
|
|
13
10
|
}
|
|
14
11
|
else {
|
|
15
|
-
await
|
|
16
|
-
await
|
|
12
|
+
await downloadAll(newPackages, options);
|
|
13
|
+
await updateDependenciesCache(newPackages, cacheFilePath, options.prebuiltBinaryProperties);
|
|
17
14
|
console.log(`Downloaded ${newPackages.length} packages`);
|
|
18
15
|
}
|
|
19
16
|
return newPackages;
|