Skip to content

Instantly share code, notes, and snippets.

@nondanee
Last active May 17, 2024 03:26
Show Gist options
  • Save nondanee/ec8ea08e9ec8c5d6e5bb42825e9cd3f0 to your computer and use it in GitHub Desktop.
Save nondanee/ec8ea08e9ec8c5d6e5bb42825e9cd3f0 to your computer and use it in GitHub Desktop.
safely replace npm registry in lock file
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const url = require('url');
const http = require('http');
const https = require('https');
const { promisify } = require('util');
const lockfile = require('@yarnpkg/lockfile');
const [
,
,
from = 'registry.npm.taobao.org',
to = 'registry.npmmirror.com',
] = process.argv;
// e.g. https://registry.npmmirror.com/semver/6.3.0
const view = async (name, version) => {
const host = /^https?:\/\//.test(to) ? to : ('https://' + to);
const options = url.parse(`${host}/${encodeURIComponent(name)}/${encodeURIComponent(version)}`);
options.timeout = 6e4;
const { request } = options.protocol === 'https:' ? https : http;
const response = await new Promise((resolve, reject) => {
request(options)
.on('response', resolve)
.on('error', reject)
.end();
});
const chunks = [];
await new Promise((resolve, reject) => {
response
.on('end', resolve)
.on('error', reject)
.on('data', (chunk) => chunks.push(chunk));
});
return JSON.parse(Buffer.concat(chunks));
};
const hit = (object) => {
const { version, resolved } = object || {};
return version && resolved && url.parse(resolved).host === from;
};
const extract = (name) => {
const index = name.indexOf('@', /^@/.test(name) ? 1 : 0);
if (index === -1) return name;
return name.slice(0, index);
};
const replace = async (fileName, parse, stringify) => {
const input = await promisify(fs.readFile)(fileName, 'utf-8');
const json = parse(input);
const infoMap = {};
const infoPromises = [];
JSON.stringify(json, (name, value) => {
name = extract(name);
const {
version,
} = value || {};
if (hit(value)) {
const key = name + '@' + version;
if (!infoMap[key]) {
infoMap[key] = view(name, version)
.then((info) => {
infoMap[key] = info;
})
.catch((e) => {
// console.log(444, e);
});
infoPromises.push(infoMap[key]);
}
}
return value;
});
await Promise.all(infoPromises);
const total = Object.keys(infoMap).length;
if (!total) {
console.log(`[${fileName}]`, 'no package need to replace');
return;
}
const errorMap = {};
JSON.stringify(json, (name, value) => {
name = extract(name);
const {
version,
integrity,
} = value || {};
if (hit(value)) {
const key = name + '@' + version;
const { dist } = infoMap[key] || {};
if (dist) {
if (integrity) {
if (integrity.indexOf('sha1-') !== -1) {
const legacyIntegrity = 'sha1-' + Buffer.from(dist.shasum, 'hex').toString('base64');
if (integrity !== legacyIntegrity) {
errorMap[key] = 'IntegrityError';
}
} else if (integrity.indexOf('sha256-') !== -1) {
if (integrity !== dist.integrity) {
errorMap[key] = 'IntegrityError';
}
}
}
} else {
errorMap[key] = 'NoInfoError';
}
if (!errorMap[key]) {
value.resolved = dist.tarball;
}
}
return value;
}, 4);
const fail = Object.keys(errorMap).length;
console.log(`[${fileName}]`, `${total - fail} package(s) replaced well`);
if (fail) console.error(`[${fileName}]`, `${fail} packages(s) replaced error`, errorMap);
const output = stringify(json);
await promisify(fs.copyFile)(fileName, fileName + '.bak');
await promisify(fs.writeFile)(fileName, output);
};
(async () => {
const npmLockFileName = 'package-lock.json';
const yarnLockFileName = 'yarn.lock';
try {
await promisify(fs.stat)(npmLockFileName);
await replace(
npmLockFileName,
JSON.parse,
output => JSON.stringify(output, null, 4) + '\n'
);
} catch (_) {}
try {
await promisify(fs.stat)(yarnLockFileName);
await replace(
yarnLockFileName,
input => lockfile.parse(input).object,
lockfile.stringify
);
} catch (_) {}
})();
{
"name": "package-lock-registry-replacer",
"version": "2.1.0",
"author": "nondanee",
"bin": "./index.js",
"dependencies": {
"@yarnpkg/lockfile": "*"
}
}
@nondanee
Copy link
Author

nondanee commented Jan 25, 2024

npx https://gist.github.com/nondanee/ec8ea08e9ec8c5d6e5bb42825e9cd3f0
npx https://gist.github.com/nondanee/ec8ea08e9ec8c5d6e5bb42825e9cd3f0 registry.nlark.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment