New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

dynamic-doctor

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dynamic-doctor - npm Package Compare versions

Comparing version 0.0.5 to 0.1.0-beta.0

dist/src/lib/commands/sanity/check-dependencies/__fixtures__/fixtures.d.ts

437

dist/index.js

@@ -5,3 +5,3 @@ #! /usr/bin/env node

var commander = require('commander');
var enquirer = require('enquirer');
var pkg = require('enquirer');
var fs = require('fs');

@@ -14,2 +14,4 @@ var child_process = require('child_process');

var promises = require('fs/promises');
var satisfies = require('semver/functions/satisfies');
var archy = require('archy');

@@ -111,15 +113,11 @@ const getPackageManagerVersion = (packageManager) => {

console.log(...args);
this.newLine();
}
static error(...args) {
console.log(chalk.red(...args));
this.newLine();
}
static success(...args) {
console.log(chalk.green(...args));
this.newLine();
}
static warning(...args) {
console.log(chalk.yellow(...args));
this.newLine();
}

@@ -132,5 +130,3 @@ static dashedLine() {

}
this.newLine();
console.log('─'.repeat(columns));
this.newLine();
}

@@ -563,4 +559,5 @@ static newLine() {

const { prompt } = pkg;
const startDynamicDoctor = async () => {
const { confirm } = await enquirer.prompt({
const { confirm } = await prompt({
type: 'confirm',

@@ -592,3 +589,3 @@ name: 'confirm',

DoctorLogger.error('An error occurred while running dynamic doctor.');
const { confirm: seeLogsConfirm } = await enquirer.prompt({
const { confirm: seeLogsConfirm } = await prompt({
type: 'confirm',

@@ -616,2 +613,423 @@ name: 'confirm',

const ROOT_REF = '.';
const REF_SEPARATOR = ':';
const DYNAMIC_PACKAGES = [/@dynamic-labs\/.*/];
const IGNORE_PACKAGES = [
'@dynamic-labs/sdk-api',
'@dynamic-labs/iconic',
];
const getModuleReferenceRecursive = (moduleTree, ref, originalRef = ref) => {
// Return the root module tree
if (ref === ROOT_REF) {
return moduleTree;
}
// Split the ref into module parts
const refParts = ref.split(REF_SEPARATOR);
// Grab the left most module
let nextModule = refParts.shift();
if (nextModule === ROOT_REF) {
nextModule = refParts.shift();
}
if (nextModule) {
// As we are descending the tree, we need to make sure the next module exists
if (!moduleTree.modules[nextModule]) {
throw new Error(`Invalid ref: ${originalRef}, could not find next module: ${nextModule}`);
}
// If there are no more parts, we have found the module
if (refParts.length === 0) {
return moduleTree.modules[nextModule];
}
else {
// Otherwise, recurse down the tree
return getModuleReferenceRecursive(moduleTree.modules[nextModule], refParts.join(REF_SEPARATOR), originalRef);
}
}
// If we have not returned by now, the ref is invalid
throw new Error(`Invalid ref: ${originalRef}`);
};
/**
* Get the module reference in the tree given a ref string
*
* This does not resolve for missing modules, the module MUST exist in the tree
* @example getModuleReference('module1:module2:module3', moduleTree)
* @param ref A colon separated string of module names to traverse
* @param moduleTree The root module tree
* @returns The module tree at the ref
*/
const getModuleReference = (moduleTree, ref) => getModuleReferenceRecursive(moduleTree, ref);
const getNextRefStep = (ref) => {
const modules = ref.split(REF_SEPARATOR);
modules.pop(); // Drop last element
const nextRef = modules.join(REF_SEPARATOR);
return nextRef;
};
const resolveModuleRecursive = (moduleTree, dependencyRef, moduleName, rootModuleTree = moduleTree) => {
const currentModuleTree = getModuleReference(rootModuleTree, dependencyRef);
if (currentModuleTree.modules[moduleName]) {
return `${dependencyRef}${REF_SEPARATOR}${moduleName}`;
}
else if (dependencyRef === '.') {
return null;
}
const nextRef = getNextRefStep(dependencyRef);
const nextModuleTree = getModuleReference(rootModuleTree, nextRef);
return resolveModuleRecursive(nextModuleTree, nextRef, moduleName, rootModuleTree);
};
const resolveModule = (moduleTree, startingRef, moduleName) => resolveModuleRecursive(moduleTree, startingRef, moduleName);
const resolveModuleTreeModule = (type, moduleName, module, moduleTree) => {
const depType = type === 'dependency' ? 'dependencies' : 'peerDependencies';
const deps = moduleTree[depType];
if (deps && deps[moduleName]) {
moduleTree.resolved[moduleName] = {
type,
ref: `${moduleTree.ref}${REF_SEPARATOR}${moduleName}`,
optional: false,
satisfied: satisfies(module.version, deps[moduleName]),
requiredVersion: deps[moduleName],
installedVersion: module.version,
};
}
};
const resolveModuleTreeModules = (moduleTree, rootModuleTree = moduleTree) => {
Object.entries(moduleTree.modules).forEach(([moduleName, module]) => {
resolveModuleTreeModule('dependency', moduleName, module, moduleTree);
resolveModuleTreeModule('peerDependency', moduleName, module, moduleTree);
resolveModuleTreeRecursive(module, rootModuleTree);
});
};
const resolveModuleTreeDependenciesModule = (type, moduleTree, rootModuleTree) => {
const depType = type === 'dependency' ? 'dependencies' : 'peerDependencies';
const deps = moduleTree[depType];
if (deps) {
Object.entries(deps).forEach(([moduleName, version]) => {
if (!moduleTree.resolved[moduleName]) {
const resolvedRef = resolveModule(rootModuleTree, moduleTree.ref, moduleName);
const optional = moduleTree.peerDependenciesMeta?.[moduleName]?.optional ?? false;
moduleTree.resolved[moduleName] = {
type,
ref: resolvedRef,
optional,
satisfied: false,
requiredVersion: version,
installedVersion: null,
};
if (resolvedRef) {
const module = getModuleReference(rootModuleTree, resolvedRef);
moduleTree.resolved[moduleName].satisfied = satisfies(module.version, version);
moduleTree.resolved[moduleName].installedVersion = module.version;
module.isDynamic = moduleTree.isDynamic;
resolveModuleTreeRecursive(module, rootModuleTree);
}
}
});
}
};
const resolveModuleTreeDependencies = (moduleTree, rootModuleTree = moduleTree) => {
resolveModuleTreeDependenciesModule('dependency', moduleTree, rootModuleTree);
resolveModuleTreeDependenciesModule('peerDependency', moduleTree, rootModuleTree);
};
const resolveModuleTreeRecursive = (moduleTree, rootModuleTree = moduleTree) => {
resolveModuleTreeModules(moduleTree, rootModuleTree);
resolveModuleTreeDependencies(moduleTree, rootModuleTree);
return moduleTree;
};
const resolveModuleTree = (moduleTree) => {
moduleTree = structuredClone(moduleTree);
return resolveModuleTreeRecursive(moduleTree);
};
const filterDynamicModules = (moduleTree) => {
return Object.fromEntries(Object.entries(moduleTree.modules).filter(([, module]) => module.isDynamic));
};
const auditResultsRecursive = (moduleTree, auditResultsStore = {}) => {
const dynamicModuleTree = filterDynamicModules(moduleTree);
Object.values(dynamicModuleTree).forEach((module) => {
Object.entries(module.resolved).forEach(([resolvedModuleName, resolvedModule]) => {
if (!resolvedModule.satisfied && !resolvedModule.optional) {
const key = `${module.ref}${REF_SEPARATOR}${resolvedModuleName}`;
auditResultsStore[key] = {
...resolvedModule,
name: resolvedModuleName,
};
if (resolvedModule.ref) {
const referencedModule = getModuleReference(moduleTree, resolvedModule.ref);
auditResultsStore[key].dependency = referencedModule;
auditResultsRecursive(referencedModule, auditResultsStore);
}
}
});
});
return auditResultsStore;
};
const auditResults = (moduleTree) => {
return auditResultsRecursive(moduleTree);
};
const isDynamicPackage = (...dynamicRefs) => {
const refs = dynamicRefs.flat().map((ref) => ref.split(':'));
return refs.some((ref) => {
return ref.some((refPart) => {
return DYNAMIC_PACKAGES.some((p) => p.test(refPart) && !isIgnoredPackage(refPart));
});
});
};
const isIgnoredPackage = (packageName) => {
return IGNORE_PACKAGES.includes(packageName);
};
//////////////////////////////
// Helper Methods
//////////////////////////////
/**
* Reads a json file and returns the parsed json.
* @param filePath
* @returns
*/
const readJsonFile = (filePath) => {
return JSON.parse(fs.readFileSync(filePath, 'utf8').trim());
};
/**
* Merges the scoped packages into the packages array.
* @param basePath
* @param packages
* @param dir
* @returns
*/
const mergeScopes = (basePath, packages, dir) => {
if (/^@/.test(dir)) {
const scopedSuffixPackages = fs
.readdirSync(path.join(basePath, dir))
.filter((packageName) => !/^\./.test(packageName));
return packages.concat(scopedSuffixPackages.map((p) => path.join(dir, p)));
}
else {
return packages.concat(dir);
}
};
//////////////////////////////
// END Helper Methods
//////////////////////////////
const initialBranch = () => structuredClone({
name: '',
version: '',
dependencies: {},
isDynamic: false,
peerDependencies: {},
peerDependenciesMeta: {},
resolved: {},
ref: '',
moduleLineage: [],
modules: {},
modulePath: '',
});
// This variable will be mutated by the buildModuleTree function
const moduleTreeStore = initialBranch();
const resolveLineage = (modulePath) => {
const lineage = modulePath
.split(/\/?node_modules\//)
.filter((p) => p && p !== ROOT_REF);
lineage.unshift(ROOT_REF);
return lineage;
};
const buildModuleTreeRecursive = (basePath, rootBasePath, currentModuleBranch = moduleTreeStore, isDynamicFlag = false) => {
const packageJsonPath = path.join(basePath, 'package.json');
const nodeModulesPath = path.join(basePath, 'node_modules');
const isRootPkg = basePath === rootBasePath;
if (fs.existsSync(packageJsonPath)) {
const modulePath = isRootPkg ? '.' : path.relative(rootBasePath, basePath);
const packageJson = readJsonFile(packageJsonPath);
const ref = resolveLineage(modulePath).join(REF_SEPARATOR);
isDynamicFlag = isDynamicFlag || isDynamicPackage(ref);
// If we are a nested module, set the moduleTree branch to the current module.
currentModuleBranch = isRootPkg
? currentModuleBranch
: (currentModuleBranch['modules'][packageJson.name] = initialBranch());
Object.assign(currentModuleBranch, {
name: packageJson.name,
version: packageJson.version,
isDynamic: isDynamicFlag,
dependencies: packageJson.dependencies,
peerDependencies: packageJson.peerDependencies,
peerDependenciesMeta: packageJson.peerDependenciesMeta,
ref,
moduleLineage: resolveLineage(modulePath),
modules: {},
modulePath,
});
if (fs.existsSync(nodeModulesPath)) {
const nodeModulesPackages = fs.readdirSync(nodeModulesPath);
// Filter out hidden directories (eg. .bin)
// Get fully scoped package names (eg. @dynamic-labs/sdk-api)
const nodules = nodeModulesPackages
.filter((dir) => !/^\./.test(dir)) // no hidden directories (eg. .bin)
.reduce((packages, dir) => mergeScopes(nodeModulesPath, packages, dir), []);
// Recurse through modules
nodules.forEach((nmPackage) => buildModuleTreeRecursive(path.join(nodeModulesPath, nmPackage), rootBasePath, currentModuleBranch, isDynamicFlag));
}
}
return currentModuleBranch;
};
const buildModuleTree = (basePath = process.cwd(), rootBasePath = process.cwd()) => buildModuleTreeRecursive(basePath, rootBasePath);
// const isOk = () => {
// return (
// !result.invalidDepVersion.length &&
// !result.invalidPeerVersion.length &&
// !result.missingDep.length &&
// !result.missingPeer.length
// );
// };
// export const reporter = () => {
// // Emoki check
// const status = chalk.bold(
// isOk() ? chalk.green('✅ OK') : chalk.red('❌ FAIL'),
// );
// console.log(
// `Sanity Checking ${chalk.bold('Dynamic SDK')} Dependencies... ${status}`,
// );
// if (isOk()) {
// return;
// }
// report('dependencies', false, result.invalidDepVersion);
// report('peer dependencies', false, result.invalidPeerVersion);
// report('dependencies', true, result.missingDep);
// report('peer dependencies', true, result.missingPeer);
// console.log(
// chalk.magentaBright(
// '🛠 Ensure all dependencies are installed and meet version requirements.',
// ),
// );
// DoctorLogger.newLine();
// };
// const report = (
// depType: 'peer dependencies' | 'dependencies',
// missing: boolean,
// resultObj:
// | Result['invalidPeerVersion']
// | Result['invalidDepVersion']
// | Result['missingDep']
// | Result['missingPeer'],
// ) => {
// if (!resultObj.length) {
// return;
// }
// DoctorLogger.dashedLine();
// console.log(chalk.bold(`${missing ? 'Missing' : 'Incorrect'} ${depType}:`));
// resultObj.forEach(({ name, lineage, ...rest }) => {
// const { requiredVersion, version } = rest as
// | Result['invalidDepVersion'][number]
// | Result['invalidPeerVersion'][number];
// lineage.push(name);
// const hierarchy: Data = lineage.reduceRight((acc, curr) => {
// if (isEmpty(acc)) {
// const label = `${curr}${version ? `@${version}` : ''}`;
// acc['label'] = chalk.yellow(
// `${label} ${
// requiredVersion
// ? chalk.red(`Incorrect version Required: ${requiredVersion}`)
// : ''
// }`,
// );
// return acc;
// }
// return {
// label: curr,
// nodes: [acc],
// };
// }, {} as Data);
// console.log(archy(hierarchy));
// });
// process.exitCode = 1;
// };
const isDataObj = (obj) => {
if (typeof obj === 'string' || !obj) {
return false;
}
return true;
};
const getNodeByLabel = (label, data) => {
const existingNode = data.nodes?.find((node) => isDataObj(node) && node.label === label);
if (isDataObj(existingNode)) {
return existingNode;
}
const newNode = {
label,
nodes: [],
};
data.nodes?.push(newNode);
return newNode;
};
const capitalizeFirstLetter = (str) => {
return str.charAt(0).toUpperCase() + str.slice(1);
};
const getLabel = (ref, auditResult) => {
const target = ref.split(REF_SEPARATOR).pop();
if (ref !== target) {
return ref;
}
if (!auditResult.dependency) {
return chalk.red(`${chalk.yellow(target)} ${capitalizeFirstLetter(auditResult.type)} not found. Required version: ${auditResult.requiredVersion}`);
}
return chalk.red(`${chalk.yellow(`${target}@${auditResult.installedVersion}`)} ${capitalizeFirstLetter(auditResult.type)} is not the required version: ${auditResult.requiredVersion}`);
};
// Converts AuditResults to archy.Data using the key of AuditResults seperated by REF_SEPARATOR as nodes
const convertToArchy = (auditResults, archyData = {
label: '',
nodes: [],
}) => {
Object.entries(auditResults).forEach(([key, value]) => {
const refParts = key.split(REF_SEPARATOR);
let data = archyData;
while (refParts.length > 0) {
const [parent, child] = refParts;
if (parent === ROOT_REF) {
data = getNodeByLabel(child, archyData);
}
else if (child) {
data.nodes?.push({
label: getLabel(child, value),
nodes: [],
});
}
refParts.shift();
}
});
return archyData;
};
const reporter = (auditResults) => {
return archy(convertToArchy(auditResults));
};
const checkDependencies = (path) => {
try {
const moduleTree = buildModuleTree(path, path);
const resolvedModuleTree = resolveModuleTree(moduleTree);
const audit = auditResults(resolvedModuleTree);
//console.log(JSON.stringify(resolveModuleTree(rawTree), null, 2));
// const tree = annotateTree(rawTree);
// const filteredTree = Object.fromEntries(
// Object.entries(tree).filter(([, value]) => value?.meta?.isDynamic),
// );
// recurseVersionCheck(filteredTree);
// reporter();
//console.log(reporter(audit));
console.log(reporter(audit));
}
catch (e) {
DoctorLogger.error(e);
throw e;
}
};
const sanityCommand = () => {
const command = new commander.Command('sanity');
command.argument('[path]', 'Path of the project using Dynamic SDK (defaults to current directory)');
command.action(checkDependencies);
command.enablePositionalOptions();
return command;
};
class DynamicCLI {

@@ -624,2 +1042,3 @@ constructor() {

});
this.program.addCommand(sanityCommand());
}

@@ -626,0 +1045,0 @@ run() {

4

package.json

@@ -12,3 +12,3 @@ {

},
"version": "0.0.5",
"version": "0.1.0-beta.0",
"license": "MIT",

@@ -42,2 +42,3 @@ "bin": {

"dependencies": {
"archy": "^1.0.0",
"chalk": "4.1.2",

@@ -55,2 +56,3 @@ "commander": "^10.0.1",

"@rollup/plugin-typescript": "^11.1.2",
"@types/archy": "^0.0.36",
"@types/chalk": "^2.2.0",

@@ -57,0 +59,0 @@ "@types/commander": "^2.12.2",

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc