sync-npm-packages
Version:
Sync released npm packages to a mirror site.
128 lines (123 loc) • 3.47 kB
JavaScript
import { readFile } from 'node:fs/promises';
import { request } from 'node:https';
import process from 'node:process';
import { glob } from 'tinyglobby';
import { createConfigLoader } from 'unconfig';
function toArray(value) {
value ??= [];
return Array.isArray(value) ? value : [value];
}
function unique(array) {
return Array.from(new Set(array));
}
function isValidPublicPackage(packageJson) {
if (packageJson.private) return false;
return !!(packageJson.name && packageJson.version);
}
const SUPPORTED_TARGETS = ["npmmirror"];
function assertSyncTarget(target) {
if (!target || !SUPPORTED_TARGETS.includes(target)) {
throw new Error(
`Required option target to be one of ${SUPPORTED_TARGETS.join(", ")}`
);
}
}
const IGNORE_NODE_MODULES = "**/node_modules/**";
const DEFAULT_IGNORE = [
IGNORE_NODE_MODULES,
"**/.git/**",
"**/docs/**",
"**/tests/**",
"**/examples/**",
"**/fixtures/**",
"**/playground/**"
];
const GLOB_PACKAGE_JSON = "**/package.json";
async function syncPackage2NpmMirror(packageName) {
const p = new Promise((resolve) => {
const req = request({
method: "PUT",
path: `/${packageName}/sync_upstream=true`,
host: "registry-direct.npmmirror.com",
protocol: "https:",
headers: {
"content-length": 0
}
});
req.write("");
req.on("close", () => {
resolve();
});
req.end();
});
return p;
}
async function syncNpmPackages(input, options) {
const packages = unique(toArray(input));
assertSyncTarget(options.target);
return Promise.all(packages.map((v) => syncPackage2NpmMirror(v)));
}
async function getValidPackageNames(options = {}) {
const {
cwd = process.cwd(),
defaultIgnore: useDefaultIgnore = true,
ignore: userIgnore = [],
include = [],
exclude = [],
withOptional = false
} = options;
const ignore = toArray(userIgnore);
if (useDefaultIgnore) {
ignore.push(...DEFAULT_IGNORE);
} else {
ignore.push(IGNORE_NODE_MODULES);
}
const files = await glob(GLOB_PACKAGE_JSON, {
cwd,
ignore,
absolute: true,
onlyFiles: true
});
const packages = [...toArray(include)];
for await (const file of files) {
const content = await readFile(file, "utf-8");
const packageJson = JSON.parse(content);
if (isValidPublicPackage(packageJson)) {
packages.push(packageJson.name);
if (withOptional) {
packages.push(...Object.keys(packageJson.optionalDependencies || {}));
}
}
}
return unique(packages.filter((pkg) => !toArray(exclude).includes(pkg)));
}
async function syncNpmPackagesAuto(options) {
assertSyncTarget(options.target);
const packages = await getValidPackageNames(options);
return syncNpmPackages(packages, options);
}
function defineConfig(config = {}) {
return config;
}
async function resolveConfig(cliConfig = {}) {
const loader = createConfigLoader({
sources: [
{
files: ["sync.config"],
extensions: ["mts", "cts", "ts", "mjs", "cjs", "js", "json"]
},
{
files: [".syncrc"],
extensions: ["json"]
}
],
cwd: process.cwd(),
merge: false
});
const { config = {}, sources = [] } = await loader.load();
return sources.length ? {
...config,
...cliConfig
} : cliConfig;
}
export { syncNpmPackagesAuto as a, assertSyncTarget as b, defineConfig as d, getValidPackageNames as g, resolveConfig as r, syncNpmPackages as s };