codependence
Version:
Checks `codependencies` in package.json files to ensure dependencies are up-to-date 🤼♀️
398 lines (392 loc) • 13.8 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
script: () => script
});
module.exports = __toCommonJS(src_exports);
// src/program.ts
var import_commander = require("commander");
var import_cosmiconfig = require("cosmiconfig");
var import_ora = __toESM(require("ora"), 1);
var import_gradient_string2 = __toESM(require("gradient-string"), 1);
// src/scripts/utils.ts
var import_gradient_string = __toESM(require("gradient-string"), 1);
var logger = ({ type, section = "", message, err = "", isDebugging = false }) => {
const emoji = `\u{1F93C}\u200D\u2640\uFE0F`;
const gap = ` => `;
const debugMsg = isDebugging ? "debugging:" : "";
const sectionMsg = section.length ? `${section}:` : "";
const firstLine = `codependence:${debugMsg}${sectionMsg}`;
const secondLine = message ? `${emoji}${gap}${message}` : "";
if (type === "error") {
console.error(import_gradient_string.default.passion(firstLine));
if (secondLine)
console.error(secondLine);
if (err)
console.error(err);
} else if (type === "debug") {
console.debug(import_gradient_string.default.passion(firstLine));
if (secondLine)
console.debug(secondLine);
} else if (type === "info") {
console.info(import_gradient_string.default.teen(firstLine));
if (secondLine)
console.info(secondLine);
} else {
console.log(import_gradient_string.default.teen(firstLine));
if (secondLine)
console.log(secondLine);
}
};
var writeConsoleMsgs = (packageName, depList) => {
if (!depList.length)
return;
Array.from(depList, ({ name: depName, expected, actual }) => {
logger({
type: "log",
section: packageName,
message: `${depName} version is incorrect!`
});
console.log(`\u{1F93C}\u200D\u2640\uFE0F => Found ${actual} and should be ${expected}`);
});
};
// src/scripts/core.ts
var import_fs = require("fs");
var import_validate_npm_package_name = __toESM(require("validate-npm-package-name"), 1);
var import_execa = require("execa");
var import_fast_glob = __toESM(require("fast-glob"), 1);
var { sync: glob } = import_fast_glob.default;
var constructVersionMap = async ({
codependencies,
exec = import_execa.execa,
debug = false,
yarnConfig = false,
isTesting = false,
validate = import_validate_npm_package_name.default
}) => {
const updatedCodeDependencies = await Promise.all(
codependencies.map(async (item) => {
try {
if (typeof item === "object" && Object.keys(item).length === 1) {
return item;
} else if (typeof item === "string" && item.length > 1 && !item.includes(" ")) {
const { validForNewPackages, validForOldPackages, errors } = validate(item);
const isValid = [validForNewPackages, validForOldPackages].every((valid) => valid === true);
if (!isValid)
throw new Error(errors?.join(", "));
const runner = !yarnConfig ? "npm" : "yarn";
const cmd = !yarnConfig ? ["view", item, "version", "latest"] : ["npm", "info", item, "--fields", "version", "--json"];
const { stdout = "" } = await exec(runner, cmd);
const version = !yarnConfig ? stdout.replace("\n", "") : JSON.parse(stdout.replace("\n", ""))?.version;
if (version)
return { [item]: version };
throw `${version}`;
} else {
throw "invalid item type";
}
} catch (err) {
if (debug)
logger({
type: "error",
section: `constructVersionMap`,
message: err.toString(),
isDebugging: debug
});
logger({
type: "error",
section: `constructVersionMap`,
message: `there was an error retrieving ${item}`
});
console.error(`\u{1F93C}\u200D\u2640\uFE0F => Is \u261D\uFE0F a private package? Does that name look correct? \u{1F9D0}`);
console.error(
`\u{1F93C}\u200D\u2640\uFE0F => Read more about configuring dependencies here: https://github.com/yowainwright/codependence#debugging`
);
if (isTesting)
return {};
process.exit(1);
}
})
);
const versionMap = updatedCodeDependencies.reduce(
(acc = {}, item) => {
const [name] = Object.keys(item);
const version = item?.[name];
return { ...acc, ...name && version ? { [name]: version } : {} };
},
{}
);
return versionMap;
};
var constructVersionTypes = (version) => {
const versionCharacters = version.split("");
const [firstCharacter, ...rest] = versionCharacters;
const specifier = ["^", "~"].includes(firstCharacter) ? firstCharacter : "";
const hasSpecifier = specifier.length === 1;
const characters = rest.join("");
const exactVersion = hasSpecifier ? characters : version;
const bumpVersion = version;
return {
bumpCharacter: specifier,
bumpVersion,
exactVersion
};
};
var constructDepsToUpdateList = (dep = {}, versionMap) => {
if (!Object.keys(dep).length)
return [];
const versionList = Object.keys(versionMap);
return Object.entries(dep).map(([name, version]) => {
const { exactVersion, bumpCharacter, bumpVersion } = constructVersionTypes(version);
return { name, exactVersion, bumpCharacter, bumpVersion };
}).filter(({ name, exactVersion }) => versionList.includes(name) && versionMap[name] !== exactVersion).map(({ name, bumpCharacter, bumpVersion }) => ({
name,
actual: bumpVersion,
exact: versionMap[name],
expected: `${bumpCharacter}${versionMap[name]}`
}));
};
var constructDeps = (json, depName, depList) => depList?.length ? depList.reduce(
(newJson, { name, expected: version }) => {
return {
...json[depName],
...newJson,
[name]: version
};
},
{}
) : json[depName];
var constructJson = (json, depsToUpdate, isDebugging = false) => {
const { depList, devDepList, peerDepList } = depsToUpdate;
const dependencies = constructDeps(json, "dependencies", depList);
const devDependencies = constructDeps(json, "devDependencies", devDepList);
const peerDependencies = constructDeps(json, "peerDependencies", peerDepList);
if (isDebugging) {
logger({
type: "debug",
section: "constructJson",
isDebugging
});
console.debug({
dependencies,
devDependencies,
peerDependencies
});
}
return {
...json,
...dependencies ? { dependencies } : {},
...devDependencies ? { devDependencies } : {},
...peerDependencies ? { peerDependencies } : {}
};
};
var checkDependenciesForVersion = (versionMap, json, options) => {
const { name, dependencies, devDependencies, peerDependencies } = json;
const { isUpdating, isDebugging, isSilent, isTesting } = options;
if (!dependencies && !devDependencies && !peerDependencies)
return false;
const depList = constructDepsToUpdateList(dependencies, versionMap);
const devDepList = constructDepsToUpdateList(devDependencies, versionMap);
const peerDepList = constructDepsToUpdateList(peerDependencies, versionMap);
if (isDebugging) {
logger({
type: "debug",
isDebugging,
section: "checkDependenciesForVersion"
});
console.debug({
depList,
devDepList,
peerDepList
});
}
if (!depList.length && !devDepList.length && !peerDepList.length) {
return false;
}
if (!isSilent)
Array.from([depList, devDepList, peerDepList], (list) => writeConsoleMsgs(name, list));
if (isUpdating) {
const updatedJson = constructJson(json, { depList, devDepList, peerDepList }, isDebugging);
const { path, ...newJson } = updatedJson;
if (!isTesting) {
(0, import_fs.writeFileSync)(path, JSON.stringify(newJson, null, 2).concat("\n"));
} else {
logger({
type: "info",
section: "checkDependenciesForVersion:test-writeFileSync:",
message: path
});
}
}
return true;
};
var checkMatches = ({
versionMap,
rootDir,
files,
isUpdating = false,
isDebugging = false,
isSilent = true,
isCLI = false,
isTesting = false
}) => {
const packagesNeedingUpdate = files.map((file) => {
const path = `${rootDir}${file}`;
const packageJson = (0, import_fs.readFileSync)(path, "utf8");
const json = JSON.parse(packageJson);
return { ...json, path };
}).filter(
(json) => checkDependenciesForVersion(versionMap, json, {
isUpdating,
isDebugging,
isSilent,
isTesting
})
);
if (isDebugging) {
logger({
type: "debug",
section: "checkMatches",
isDebugging,
message: "see updates"
});
console.debug({ packagesNeedingUpdate });
}
const isOutOfDate = packagesNeedingUpdate.length > 0;
if (isOutOfDate && !isUpdating) {
logger({
type: "error",
message: "Dependencies are not correct. \u{1F61E}"
});
if (isCLI)
process.exit(1);
} else if (isOutOfDate) {
logger({
type: "info",
message: "Dependencies were not correct but should be updated! Check your git status. \u{1F603}"
});
} else {
logger({
type: "log",
message: "No dependency issues found! \u{1F44C}"
});
}
};
var checkFiles = async ({
codependencies,
files: matchers = ["package.json"],
rootDir = "./",
ignore = ["**/node_modules/**"],
update = false,
debug = false,
silent = false,
isCLI = false,
yarnConfig = false,
isTesting = false
}) => {
try {
const files = glob(matchers, { cwd: rootDir, ignore });
if (!codependencies)
throw '"codependencies" are required';
const versionMap = await constructVersionMap({
codependencies,
debug,
yarnConfig,
isTesting
});
checkMatches({
versionMap,
rootDir,
files,
isCLI,
isSilent: silent,
isUpdating: update,
isDebugging: debug,
isTesting
});
} catch (err) {
if (debug) {
logger({
type: "error",
isDebugging: true,
section: "checkFiles",
message: err.toString()
});
}
}
};
var script = checkFiles;
// src/program.ts
async function action(options = {}) {
const explorer = (0, import_cosmiconfig.cosmiconfigSync)("codependence");
const result = options?.searchPath ? explorer.search(options.searchPath) : explorer.search();
const { config: pathConfig = {} } = options?.config ? explorer.load(options?.config) : {};
const updatedConfig = {
...!Object.keys(pathConfig).length ? result?.config : {},
...pathConfig?.codependence ? { ...pathConfig.codependence } : pathConfig,
...options,
isCLI: true
};
const {
config: usedConfig,
searchPath: usedSearchPath,
isTestingCLI,
isTestingAction,
...updatedOptions
} = updatedConfig;
if (isTestingCLI) {
console.info({ updatedOptions });
return;
}
if (isTestingAction)
return updatedOptions;
try {
if (!updatedOptions.codependencies)
throw '"codependencies" is required';
const spinner = (0, import_ora.default)(`\u{1F93C}\u200D\u2640\uFE0F ${import_gradient_string2.default.teen(`codependence`)} wrestling...
`).start();
await script(updatedOptions);
spinner.succeed(`\u{1F93C}\u200D\u2640\uFE0F ${import_gradient_string2.default.teen(`codependence`)} pinned!`);
} catch (err) {
logger({
type: "error",
section: "cli:error",
message: err.toString()
});
}
}
import_commander.program.description(
"Codependency, for code dependency. Checks `coDependencies` in package.json files to ensure dependencies are up-to-date"
).option("-t, --isTestingCLI", "enable CLI only testing").option("--isTesting", "enable running fn tests w/o overwriting").option("-f, --files [files...]", "file glob pattern").option("-u, --update", "update dependencies based on check").option("-r, --rootDir <rootDir>", "root directory to start search").option("-i, --ignore [ignore...]", "ignore glob pattern").option("--debug", "enable debugging").option("--silent", "enable mainly silent logging").option("-cds, --codependencies [codependencies...]", "deps to check").option("-c, --config <config>", "path to a config file").option("-s, --searchPath <searchPath>", "path to do a config file search").option("-y, --yarnConfig", "enable yarn config support").action(action).parse(process.argv);
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
script
});
;