@nodesecure/tarball
Version:
NodeSecure tarball scanner
111 lines • 3.87 kB
JavaScript
// Import Third-party Dependencies
import { ManifestManager, parseNpmSpec } from "@nodesecure/mama";
// CONSTANTS
export const NODE_BUILTINS = new Set([
"assert",
"buffer",
"child_process",
"cluster",
"console",
"constants",
"crypto",
"dgram",
"dns",
"domain",
"events",
"fs",
"http",
"https",
"module",
"net",
"os",
"path",
"punycode",
"querystring",
"readline",
"repl",
"stream",
"string_decoder",
"sys",
"timers",
"tls",
"tty",
"url",
"util",
"vm",
"zlib",
"freelist",
"v8",
"process",
"inspector",
"async_hooks",
"http2",
"perf_hooks",
"trace_events",
"worker_threads",
"node:test",
"wasi",
"diagnostics_channel"
]);
const kFileExtensions = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".node", ".json"];
const kExternalModules = new Set(["http", "https", "net", "http2", "dgram", "child_process"]);
export function analyzeDependencies(sourceDependencies, options) {
const { mama, tryDependencies } = options;
const { dependencies, devDependencies, nodejsImports = {} } = mama;
// See: https://nodejs.org/api/packages.html#subpath-imports
const subpathImportsDependencies = Object.fromEntries(sourceDependencies
.filter((name) => isAliasFileModule(name) && name in nodejsImports)
.map((name) => buildSubpathDependency(name, nodejsImports)));
const thirdPartyDependenciesAliased = new Set(Object.values(subpathImportsDependencies).filter((mod) => !isFile(mod)));
const thirdPartyDependencies = sourceDependencies.flatMap((sourceName) => {
const name = dependencies.includes(sourceName) ?
sourceName :
parseNpmSpec(sourceName)?.name ?? sourceName;
return isFile(name) ||
isCoreModule(name) ||
devDependencies.includes(name) ||
tryDependencies.has(name) ?
[] : name;
});
const unusedDependencies = difference(dependencies.filter((name) => !name.startsWith("@types")), [...thirdPartyDependencies, ...thirdPartyDependenciesAliased]);
const missingDependencies = [
...new Set(difference(thirdPartyDependencies, dependencies))
]
.filter((name) => !(name in nodejsImports) && !thirdPartyDependenciesAliased.has(name));
const nodeDependencies = sourceDependencies.filter((name) => isCoreModule(name));
const hasMissingOrUnusedDependency = unusedDependencies.length > 0 ||
missingDependencies.length > 0;
return {
nodeDependencies,
thirdPartyDependencies: [...new Set(thirdPartyDependencies)],
subpathImportsDependencies,
unusedDependencies,
missingDependencies,
flags: {
hasExternalCapacity: nodeDependencies.some((depName) => kExternalModules.has(depName)),
hasMissingOrUnusedDependency
}
};
}
function difference(arr1, arr2) {
return arr1.filter((item) => !arr2.includes(item));
}
function isFile(filePath) {
return filePath.startsWith(".")
|| kFileExtensions.some((extension) => filePath.endsWith(extension));
}
function isCoreModule(moduleName) {
const cleanModuleName = moduleName.startsWith("node:") ? moduleName.slice(5) : moduleName;
// Note: We need to also check moduleName because builtins package only return true for 'node:test'.
return NODE_BUILTINS.has(cleanModuleName) || NODE_BUILTINS.has(moduleName);
}
function isAliasFileModule(moduleName) {
return moduleName.charAt(0) === "#";
}
function buildSubpathDependency(alias, nodeImports) {
const importEntry = nodeImports[alias];
return typeof importEntry === "string" ?
[alias, importEntry] :
[alias, "node" in importEntry ? importEntry.node : importEntry.default];
}
//# sourceMappingURL=analyzeDependencies.js.map