120 lines
3.4 KiB
JavaScript
120 lines
3.4 KiB
JavaScript
const path = require("path");
|
|
|
|
function getAbsolutePath(filename) {
|
|
let normalizedFilename = path.normalize(filename); // removes dot slash
|
|
let hasDotSlash = filename.startsWith("./");
|
|
return hasDotSlash ? path.join(path.resolve("."), normalizedFilename) : normalizedFilename;
|
|
}
|
|
|
|
function getRelativePath(filename) {
|
|
let normalizedFilename = path.normalize(filename); // removes dot slash
|
|
let workingDirectory = path.resolve(".");
|
|
let result = "./" + (normalizedFilename.startsWith(workingDirectory) ? normalizedFilename.substr(workingDirectory.length + 1) : normalizedFilename);
|
|
return result;
|
|
}
|
|
|
|
function getNodeModuleName(filename) {
|
|
let foundNodeModules = false;
|
|
let moduleName = [];
|
|
|
|
let s = filename.split(path.sep);
|
|
for(let entry of s) {
|
|
if(foundNodeModules) {
|
|
moduleName.push(entry);
|
|
if(!entry.startsWith("@")) {
|
|
return moduleName.join("/");
|
|
}
|
|
}
|
|
|
|
if(entry === "node_modules") {
|
|
foundNodeModules = true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* unordered */
|
|
function getDependenciesFor(filename, avoidCircular, optionsArg = {}) {
|
|
// backwards compatibility with `nodeModuleNamesOnly` boolean option
|
|
// Using `nodeModuleNames` property moving forward
|
|
if(("nodeModuleNamesOnly" in optionsArg) && !("nodeModuleNames" in optionsArg)) {
|
|
if(optionsArg.nodeModuleNamesOnly === true) {
|
|
optionsArg.nodeModuleNames = "only";
|
|
}
|
|
if(optionsArg.nodeModuleNamesOnly === false) {
|
|
optionsArg.nodeModuleNames = "exclude";
|
|
}
|
|
}
|
|
|
|
let options = Object.assign({
|
|
allowNotFound: false,
|
|
nodeModuleNames: "exclude", // also "include" or "only"
|
|
}, optionsArg);
|
|
let absoluteFilename = getAbsolutePath(filename)
|
|
|
|
try {
|
|
require(absoluteFilename);
|
|
} catch(e) {
|
|
if(e.code === "MODULE_NOT_FOUND" && options.allowNotFound) {
|
|
// do nothing
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
|
|
let mod;
|
|
for(let entry in require.cache) {
|
|
if(entry === absoluteFilename) {
|
|
mod = require.cache[entry];
|
|
break;
|
|
}
|
|
}
|
|
|
|
let dependencies = new Set();
|
|
|
|
if(!mod) {
|
|
if(!options.allowNotFound) {
|
|
throw new Error(`Could not find ${filename} in @11ty/dependency-tree`);
|
|
}
|
|
} else {
|
|
let relativeFilename = getRelativePath(mod.filename);
|
|
if(!avoidCircular) {
|
|
avoidCircular = {};
|
|
} else if(options.nodeModuleNames !== "only") {
|
|
dependencies.add(relativeFilename);
|
|
}
|
|
|
|
avoidCircular[relativeFilename] = true;
|
|
|
|
if(mod.children) {
|
|
for(let child of mod.children) {
|
|
let relativeChildFilename = getRelativePath(child.filename);
|
|
let nodeModuleName = getNodeModuleName(child.filename);
|
|
|
|
if(options.nodeModuleNames !== "exclude" && nodeModuleName) {
|
|
dependencies.add(nodeModuleName);
|
|
}
|
|
// Add dependencies of this dependency (not top level node_modules)
|
|
if(nodeModuleName === false) {
|
|
if(!dependencies.has(relativeChildFilename) && // avoid infinite looping with circular deps
|
|
!avoidCircular[relativeChildFilename] ) {
|
|
for(let dependency of getDependenciesFor(relativeChildFilename, avoidCircular, options)) {
|
|
dependencies.add(dependency);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return dependencies;
|
|
}
|
|
|
|
function getCleanDependencyListFor(filename, options = {}) {
|
|
return Array.from( getDependenciesFor(filename, null, options) );
|
|
}
|
|
|
|
module.exports = getCleanDependencyListFor;
|
|
module.exports.getNodeModuleName = getNodeModuleName; |