|
|
@@ -1,127 +1,36 @@
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
-const fs = require('fs');
|
|
|
-const path = require('path');
|
|
|
-const glob = require('glob');
|
|
|
-const semver = require('semver');
|
|
|
-
|
|
|
-const allowedOptions = {
|
|
|
- tabWidth: {
|
|
|
- description: "Tab width in spaces, passed to JSON.stringify for output.",
|
|
|
- flags: ["-tw", "--tab-width"],
|
|
|
- numArgs: 1,
|
|
|
- default: 2
|
|
|
- },
|
|
|
- write: {
|
|
|
- description: "Write changes to files.",
|
|
|
- flags: ["-w", "--write"],
|
|
|
- numArgs: 0,
|
|
|
- default: false
|
|
|
- },
|
|
|
- help: {
|
|
|
- description: "Prints the help message, options and usage info then exits.",
|
|
|
- flags: ["-h", "--help"],
|
|
|
- numArgs: 0,
|
|
|
- default: false
|
|
|
- },
|
|
|
-}
|
|
|
-
|
|
|
-const optionNames = Object.keys(allowedOptions)
|
|
|
-
|
|
|
-function printHelpMessage() {
|
|
|
- console.log("sync-package-json-deps: a script to find and fix out-of-sync dependencies in package.json files.\n")
|
|
|
- console.log("Usage: script.js [options] [glob patterns]")
|
|
|
- console.log("Example: sync-package-json-deps.js -w --tab-width 4 './packages/*/package.json' 'package.json'\n")
|
|
|
- console.log("Allowed options:")
|
|
|
- optionNames.forEach(optionName => {
|
|
|
- const option = allowedOptions[optionName]
|
|
|
- console.log(`${optionName}: ${option.description}`)
|
|
|
- console.log(`flags: ${option.flags}`)
|
|
|
- console.log(`number of arguments: ${option.numArgs}`)
|
|
|
- console.log(`default: ${option.default}\n`)
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-function getPackageFiles(globs) {
|
|
|
- const files = new Set();
|
|
|
- globs.forEach((pattern) => {
|
|
|
- glob.sync(pattern).forEach((file) => files.add(file));
|
|
|
- });
|
|
|
- return [...files];
|
|
|
-}
|
|
|
-
|
|
|
-function readPackageFile(filePath) {
|
|
|
- const content = fs.readFileSync(filePath, 'utf8');
|
|
|
- return JSON.parse(content);
|
|
|
-}
|
|
|
-
|
|
|
-function writePackageFile(filePath, content, tabWidth) {
|
|
|
- const json = JSON.stringify(content, null, Number(tabWidth));
|
|
|
- fs.writeFileSync(filePath, json, 'utf8');
|
|
|
-}
|
|
|
-
|
|
|
-function collectDependencies(packageFiles) {
|
|
|
- const allDependencies = {};
|
|
|
-
|
|
|
- packageFiles.forEach((file) => {
|
|
|
- const pkg = readPackageFile(file);
|
|
|
- ['dependencies', 'devDependencies', 'peerDependencies'].forEach((key) => {
|
|
|
- if (pkg[key]) {
|
|
|
- for (const [dep, version] of Object.entries(pkg[key])) {
|
|
|
- if (!allDependencies[dep]) {
|
|
|
- allDependencies[dep] = [];
|
|
|
- }
|
|
|
- if (!allDependencies[dep].includes(version)) {
|
|
|
- allDependencies[dep].push(version);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
-
|
|
|
- return allDependencies;
|
|
|
-}
|
|
|
-
|
|
|
-function findHighestVersion(versions) {
|
|
|
- return versions.sort(semver.rcompare)[0];
|
|
|
-}
|
|
|
-
|
|
|
-function updateDependencies(packageFiles, allDependencies, {write, tabWidth}) {
|
|
|
- let updated = false;
|
|
|
- packageFiles.forEach((file) => {
|
|
|
- const pkg = readPackageFile(file);
|
|
|
-
|
|
|
- ['dependencies', 'devDependencies', 'peerDependencies'].forEach((key) => {
|
|
|
- if (pkg[key]) {
|
|
|
- for (const dep of Object.keys(pkg[key])) {
|
|
|
- const highestVersion = findHighestVersion(allDependencies[dep]);
|
|
|
- if (pkg[key][dep] !== highestVersion) {
|
|
|
- pkg[key][dep] = highestVersion;
|
|
|
- updated = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- if (updated && write) {
|
|
|
- writePackageFile(file, pkg, tabWidth);
|
|
|
- console.log(`Updated ${file}`);
|
|
|
- }
|
|
|
- });
|
|
|
- if (updated && write) {
|
|
|
- console.log('Dependency versions synchronized.');
|
|
|
- }
|
|
|
-}
|
|
|
+const fs = require("fs");
|
|
|
+const path = require("path");
|
|
|
+const glob = require("glob");
|
|
|
+const semver = require("semver");
|
|
|
+const {
|
|
|
+ allowedOptions,
|
|
|
+ getOptName,
|
|
|
+ printHelpMessage,
|
|
|
+ mergeOptionsAndDefaults,
|
|
|
+ parseArgs
|
|
|
+} = require("./lib/options.js");
|
|
|
+const {
|
|
|
+ getPackageFiles,
|
|
|
+ readPackageFile,
|
|
|
+ writePackageFile
|
|
|
+} = require("./lib/package-files.js");
|
|
|
+const {
|
|
|
+ collectDependencies,
|
|
|
+ findHighestVersion,
|
|
|
+ updateDependencies
|
|
|
+} = require("./lib/dependencies.js");
|
|
|
|
|
|
function main(globPatterns, options) {
|
|
|
const packageFiles = getPackageFiles(globPatterns);
|
|
|
if (packageFiles.length === 0) {
|
|
|
- console.log('No package.json files found.');
|
|
|
- printHelpMessage()
|
|
|
+ console.log("No package.json files found.");
|
|
|
+ printHelpMessage();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- console.log('Found package.json files:', packageFiles);
|
|
|
+ console.log("Found package.json files:", packageFiles);
|
|
|
const allDependencies = collectDependencies(packageFiles);
|
|
|
|
|
|
for (const [dep, versions] of Object.entries(allDependencies)) {
|
|
|
@@ -133,46 +42,11 @@ function main(globPatterns, options) {
|
|
|
updateDependencies(packageFiles, allDependencies, options);
|
|
|
}
|
|
|
|
|
|
-function getOptName(flag) {
|
|
|
- return optionNames.find(optName => allowedOptions[optName].flags.includes(flag))
|
|
|
-}
|
|
|
-
|
|
|
-function parseArgs (rest, opts = {}, globs = []) {
|
|
|
- if (!rest || rest.length === 0) return {opts, globs};
|
|
|
- const current = rest[0]
|
|
|
-
|
|
|
- if (current.startsWith("-")) {
|
|
|
- const optName = getOptName(current)
|
|
|
- if (!optName) {
|
|
|
- printHelpMessage()
|
|
|
- throw new Error(`Unrecognised option: ${current}`)
|
|
|
- }
|
|
|
- const opt = allowedOptions[optName]
|
|
|
- const value = opt.numArgs > 0 ?
|
|
|
- rest.slice(1, opt.numArgs + 1) :
|
|
|
- [true];
|
|
|
- const nextRest = rest.slice((opt.numArgs || 0) + 1)
|
|
|
- return parseArgs(nextRest, {...opts, [optName]: value}, globs)
|
|
|
- }
|
|
|
-
|
|
|
- return parseArgs(rest.slice(1), opts, [...globs, current])
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-function mergeOptionsAndDefaults(options) {
|
|
|
- return optionNames.reduce((merged, optionName) => {
|
|
|
- const option = allowedOptions[optionName]
|
|
|
- const userValue = option.numArgs > 1 ? options[optionName] : options[optionName]?.[0]
|
|
|
- const optionValue = userValue || option.default;
|
|
|
- return {...merged, [optionName]: optionValue}
|
|
|
- }, {})
|
|
|
-}
|
|
|
-
|
|
|
-const {opts, globs} = parseArgs([...process.argv].slice(2));
|
|
|
+const { opts, globs } = parseArgs([...process.argv].slice(2));
|
|
|
|
|
|
if (!globs.length || opts.help) {
|
|
|
- printHelpMessage()
|
|
|
+ printHelpMessage();
|
|
|
} else {
|
|
|
- const mergedOpts = mergeOptionsAndDefaults(opts)
|
|
|
+ const mergedOpts = mergeOptionsAndDefaults(opts);
|
|
|
main(globs, mergedOpts);
|
|
|
}
|