Skip to content

Commit

Permalink
[INTERNAL] npm t8r: Add includeDeduped option
Browse files Browse the repository at this point in the history
When set to true, deduped modules are still included in the tree rather
than silently ignored. This is usefull when analyzing a projects
dependency tree. However most modules like builder and server do not
really care about deduped modules.
  • Loading branch information
RandomByte committed Dec 17, 2018
1 parent 84f7b25 commit eaa2863
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 175 deletions.
41 changes: 21 additions & 20 deletions lib/normalizer.js
Expand Up @@ -8,14 +8,13 @@ const projectPreprocessor = require("./projectPreprocessor");
* @module normalizer/normalizer
*/
const Normalizer = {

/**
* Generates a project and dependency tree via translators and configures all projects via the projectPreprocessor
*
* @param {Object} options Options
* @param {string} options.cwd Current working directory
* @param {string} options.configPath Path to configuration file
* @param {string} options.translator Translator to use
* @param {Object} [options]
* @param {string} [options.cwd] Current working directory
* @param {string} [options.configPath] Path to configuration file
* @param {string} [options.translator] Translator to use
* @returns {Promise} Promise resolving to tree object
*/
generateProjectTree: async function(options = {}) {
Expand All @@ -26,40 +25,42 @@ const Normalizer = {
}
return projectPreprocessor.processTree(tree);
},

/**
* Generates a project and dependency tree via translators
*
* @param {Object} options Options
* @param {string} options.cwd Current working directory
* @param {string} options.configPath Path to configuration file
* @param {string} options.translator Translator to use
* @param {Object} [options]
* @param {string} [options.cwd=.] Current working directory
* @param {string} [options.translator=npm] Translator to use
* @param {object} [options.translatorOptions] Options to pass to translator
* @returns {Promise} Promise resolving to tree object
*/
generateDependencyTree: async function(options = {}) {
generateDependencyTree: async function({cwd = ".", translator="npm", translatorOptions={}} = {}) {
// TODO NEXT MAJOR: Rename "translator" option to "translatorName"
log.verbose("Building dependency tree...");
const cwd = options && options.cwd || ".";

let translatorName = "npm"; // Default is npm translator
let translatorParams = [];
if (options.translator) {
const translatorOptions = options.translator.split(":");
translatorName = translatorOptions[0];
translatorParams = translatorOptions.slice(1);
let translatorName = translator;
if (translator.indexOf(":") !== -1) {
translatorParams = translator.split(":");
translatorName = translatorParams[0];
translatorParams = translatorParams.slice(1);
}

let translator;
let translatorModule;
switch (translatorName) {
case "static":
translator = require("./translators/static");
translatorModule = require("./translators/static");
break;
case "npm":
translator = require("./translators/npm");
translatorModule = require("./translators/npm");
break;
default:
return Promise.reject(new Error(`Unknown translator ${translatorName}`));
}

return translator.generateDependencyTree(cwd, translatorParams);
translatorOptions.parameters = translatorParams;
return translatorModule.generateDependencyTree(cwd, translatorOptions);
}
};

Expand Down
54 changes: 33 additions & 21 deletions lib/translators/npm.js
Expand Up @@ -9,10 +9,11 @@ const resolveModulePath = promisify(require("resolve"));
const parentNameRegExp = new RegExp(/:([^:]+):$/i);

class NpmTranslator {
constructor() {
constructor({includeDeduped}) {
this.projectCache = {};
this.projectsWoUi5Deps = [];
this.pendingDeps = {};
this.includeDeduped = includeDeduped;
}

/*
Expand Down Expand Up @@ -171,15 +172,21 @@ class NpmTranslator {
// Check whether modules has already been processed in the current subtree (indicates a loop)
if (parentPath.indexOf(`:${moduleName}:`) !== -1) {
// This is a loop => abort further processing
return cache.pPkg.then((pkg) => {
return [{
id: moduleName,
version: pkg.version,
path: modulePath,
dependencies: [],
deduped: true
}];
});
if (this.includeDeduped) {
// Add module marked as deduped
return cache.pPkg.then((pkg) => {
return {
id: moduleName,
version: pkg.version,
path: modulePath,
dependencies: [],
deduped: true
};
});
} else {
// Ignore this dependency
return Promise.resolve(null);
}
} else {
return cache.pProject;
}
Expand Down Expand Up @@ -312,15 +319,18 @@ class NpmTranslator {
const parent = this.pendingDeps[project.id].parents[i];
// Check whether modules has already been processed in the current subtree (indicates a loop)
if (parent.path.indexOf(`:${project.id}:`) !== -1) {
// This is a loop => abort further processing
const dedupedProject = {
id: project.id,
version: project.version,
path: project.path,
dependencies: [],
deduped: true
};
parent.project.dependencies.push(dedupedProject);
// This is a loop
if (this.includeDeduped) {
// Add module marked as deduped
const dedupedProject = {
id: project.id,
version: project.version,
path: project.path,
dependencies: [],
deduped: true
};
parent.project.dependencies.push(dedupedProject);
} // else: do nothing
} else {
parent.project.dependencies.push(project);
}
Expand Down Expand Up @@ -401,9 +411,11 @@ module.exports = {
* Generates a dependency tree for npm projects
*
* @param {string} dirPath Project path
* @param {object} [options]
* @param {boolean} [options.includeDeduped=false]
* @returns {Promise} Promise resolving with a dependency tree
*/
generateDependencyTree(dirPath) {
return new NpmTranslator().generateDependencyTree(dirPath);
generateDependencyTree(dirPath, options = {includeDeduped: false}) {
return new NpmTranslator(options).generateDependencyTree(dirPath);
}
};
9 changes: 6 additions & 3 deletions lib/translators/static.js
Expand Up @@ -14,11 +14,14 @@ const parseYaml = require("js-yaml").safeLoad;
* This feature is <b>EXPERIMENTAL</b> and used for testing purposes only.
*
* @param {string} dirPath Project path
* @param {Array} [translatorOptions] Configuration options
* @param {object} [options]
* @param {Array} [options.parameters] CLI configuration options
* @returns {Promise} Promise resolving with a dependency tree
*/
function generateDependencyTree(dirPath, translatorOptions = []) {
const depFilePath = translatorOptions[0] || path.join(dirPath, "projectDependencies.yaml");
function generateDependencyTree(dirPath, options = {}) {
// TODO NEXT MAJOR: "options" used to be the the parameters array. We check for that here to stay compatible:
const parameters = Array.isArray(options) ? options : options.parameters;
const depFilePath = parameters && parameters[0] || path.join(dirPath, "projectDependencies.yaml");

return new Promise(function(resolve, reject) {
fs.readFile(depFilePath, function(err, buffer) {
Expand Down

0 comments on commit eaa2863

Please sign in to comment.