Skip to content
This repository was archived by the owner on Dec 4, 2022. It is now read-only.

Commit 6858e42

Browse files
author
David First
committed
support tsx files (typescript jsx), remove Madge dependency and use dependency-tree directly instead
1 parent 4c0aaaf commit 6858e42

File tree

4 files changed

+242
-158
lines changed

4 files changed

+242
-158
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
"caporal": "^0.5.0",
3737
"chalk": "^1.1.3",
3838
"chokidar": "^1.6.1",
39+
"dependency-tree": "https://github.com/teambit/node-dependency-tree",
40+
"find-package": "^1.0.0",
3941
"fs-extra": "^4.0.2",
4042
"glob": "^7.1.1",
4143
"mockery": "^2.0.0",
@@ -45,8 +47,6 @@
4547
"require-from-string": "^1.2.1",
4648
"stack-trace": "0.0.9",
4749
"toposort-class": "^1.0.1",
48-
"madge": "^2.0.0",
49-
"find-package": "^1.0.0",
5050
"user-home": "^2.0.0"
5151
},
5252
"devDependencies": {

src/dependency-builder/build-tree.js

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
// TODO: This should be exported as a bit component
2-
31
// @flow
2+
// TODO: This should be exported as a bit component
43
import path from 'path';
5-
import madge from 'madge';
64
import fs from 'fs';
75
import findPackage from 'find-package';
86
import R from 'ramda';
7+
import generateTree from './generate-tree-madge';
98

109
/**
1110
* Group dependencies by types (files, bits, packages)
@@ -164,26 +163,27 @@ function normalizePaths(tree) {
164163
/**
165164
* Function for fetching dependency tree of file or dir
166165
* @param cwd working directory
166+
* @param consumerPath
167167
* @param filePath path of the file to calculate the dependecies
168168
* @return {Promise<{missing, tree}>}
169169
*/
170-
export default function getDependecyTree(cwd: string, consumerPath: string, filePath: string): Promise<*> {
171-
return madge(filePath, { baseDir: cwd, includeNpm: true })
172-
.then((res) => {
173-
const normalizedTree = normalizePaths(res.tree);
174-
const { groups, foundedPackages } = groupMissings(res.skipped, cwd, consumerPath);
175-
const relativeFilePath = path.relative(cwd, filePath);
176-
const tree = groupDependencyTree(normalizedTree, cwd);
177-
// Merge manually found packages with madge founded packages
178-
if (foundedPackages && !R.isEmpty(foundedPackages)) {
179-
// Madge found packages so we need to merge them with the manual
180-
if (tree[relativeFilePath].packages) {
181-
Object.assign(tree[relativeFilePath].packages, foundedPackages);
182-
// There is only manually found packages
183-
} else {
184-
tree[relativeFilePath].packages = foundedPackages;
185-
}
186-
}
187-
return { missing: groups, tree };
188-
});
170+
export default async function getDependecyTree(cwd: string, consumerPath: string, filePath: string): Promise<*> {
171+
const config = {
172+
baseDir: cwd, includeNpm: true, requireConfig: null, webpackConfig: null, visited: {}, nonExistent: [] };
173+
const result = generateTree([filePath], config);
174+
const normalizedTree = normalizePaths(result.tree);
175+
const { groups, foundedPackages } = groupMissings(result.skipped, cwd, consumerPath);
176+
const relativeFilePath = path.relative(cwd, filePath);
177+
const tree = groupDependencyTree(normalizedTree, cwd);
178+
// Merge manually found packages with madge founded packages
179+
if (foundedPackages && !R.isEmpty(foundedPackages)) {
180+
// Madge found packages so we need to merge them with the manual
181+
if (tree[relativeFilePath].packages) {
182+
Object.assign(tree[relativeFilePath].packages, foundedPackages);
183+
// There is only manually found packages
184+
} else {
185+
tree[relativeFilePath].packages = foundedPackages;
186+
}
187+
}
188+
return { missing: groups, tree };
189189
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// most of the functions in this file were taken from the Madge project: https://github.com/pahen/madge
2+
// reasons for not using Madge directly: 1) it has issues with TypeScript on Windows. 2) it has issues with tsx files
3+
4+
import os from 'os';
5+
import path from 'path';
6+
import dependencyTree from 'dependency-tree';
7+
8+
/**
9+
* Check if running on Windows.
10+
* @type {Boolean}
11+
*/
12+
const isWin = (os.platform() === 'win32');
13+
14+
/**
15+
* Check if path is from NPM folder
16+
* @param {String} path
17+
* @return {Boolean}
18+
*/
19+
function isNpmPathFunc(path) {
20+
return path.indexOf('node_modules') >= 0;
21+
}
22+
23+
/**
24+
* Sort tree.
25+
* @param {Object} tree
26+
* @return {Object}
27+
*/
28+
function sort(tree) {
29+
return Object
30+
.keys(tree)
31+
.sort()
32+
.reduce((acc, id) => {
33+
acc[id] = tree[id].sort();
34+
return acc;
35+
}, {});
36+
}
37+
38+
/**
39+
* Exclude modules from tree using RegExp.
40+
* @param {Object} tree
41+
* @param {Array} excludeRegExp
42+
* @return {Object}
43+
*/
44+
function exclude(tree, excludeRegExp) {
45+
const regExpList = excludeRegExp.map((re) => new RegExp(re));
46+
47+
function regExpFilter(id) {
48+
return regExpList.findIndex((regexp) => regexp.test(id)) < 0;
49+
}
50+
51+
return Object
52+
.keys(tree)
53+
.filter(regExpFilter)
54+
.reduce((acc, id) => {
55+
acc[id] = tree[id].filter(regExpFilter);
56+
return acc;
57+
}, {});
58+
}
59+
60+
61+
/**
62+
* Process absolute path and return a shorter one.
63+
* @param {String} absPath
64+
* @param {Object} cache
65+
* @param {String} baseDir
66+
* @return {String}
67+
*/
68+
function processPath(absPath, cache, baseDir) {
69+
if (cache[absPath]) {
70+
return cache[absPath];
71+
}
72+
73+
let relPath = path.relative(baseDir, absPath);
74+
75+
if (isWin) {
76+
relPath = relPath.replace(/\\/g, '/');
77+
}
78+
79+
cache[absPath] = relPath;
80+
81+
return relPath;
82+
}
83+
84+
/**
85+
* Convert deep tree produced by dependency-tree to a
86+
* shallow (one level deep) tree used by madge.
87+
* @param {Object} depTree
88+
* @param {Object} tree
89+
* @param {Object} pathCache
90+
* @param {String} baseDir
91+
* @return {Object}
92+
*/
93+
function convertTree(depTree, tree, pathCache, baseDir) {
94+
for (const key in depTree) {
95+
const id = processPath(key, pathCache, baseDir);
96+
97+
if (!tree[id]) {
98+
tree[id] = [];
99+
100+
for (const dep in depTree[key]) {
101+
tree[id].push(processPath(dep, pathCache, baseDir));
102+
}
103+
}
104+
105+
convertTree(depTree[key], tree, pathCache, baseDir);
106+
}
107+
108+
return tree;
109+
}
110+
111+
function getDetectiveOption(file, existingDetectiveOption) {
112+
const detectiveOption = existingDetectiveOption || {};
113+
const extension = path.extname(file);
114+
if (extension === '.tsx') {
115+
detectiveOption.ts = { ecmaFeatures: { jsx: true } };
116+
}
117+
return detectiveOption;
118+
}
119+
120+
/**
121+
* Generate the tree from the given files
122+
* @param {Array} files
123+
* @param config
124+
* @return {Object}
125+
*/
126+
export default function generateTree(files, config) {
127+
const depTree = {};
128+
const visited = {};
129+
const nonExistent = [];
130+
const npmPaths = {};
131+
const pathCache = {};
132+
133+
files.forEach((file) => {
134+
if (visited[file]) {
135+
return;
136+
}
137+
138+
const detective = getDetectiveOption(file, config.detectiveOptions);
139+
Object.assign(depTree, dependencyTree({
140+
filename: file,
141+
directory: config.baseDir,
142+
requireConfig: config.requireConfig,
143+
webpackConfig: config.webpackConfig,
144+
visited,
145+
filter: (dependencyFilePath, traversedFilePath) => {
146+
let dependencyFilterRes = true;
147+
const isNpmPath = isNpmPathFunc(dependencyFilePath);
148+
149+
if (config.dependencyFilter) {
150+
dependencyFilterRes = config.dependencyFilter(dependencyFilePath, traversedFilePath, config.baseDir);
151+
}
152+
153+
if (config.includeNpm && isNpmPath) {
154+
(npmPaths[traversedFilePath] = npmPaths[traversedFilePath] || []).push(dependencyFilePath);
155+
}
156+
157+
return !isNpmPath && (dependencyFilterRes || dependencyFilterRes === undefined);
158+
},
159+
detective,
160+
nonExistent,
161+
}));
162+
});
163+
164+
let tree = convertTree(depTree, {}, pathCache, config.baseDir);
165+
166+
for (const npmKey in npmPaths) {
167+
const id = processPath(npmKey, pathCache, config.baseDir);
168+
169+
npmPaths[npmKey].forEach((npmPath) => {
170+
tree[id].push(processPath(npmPath, pathCache, config.baseDir));
171+
});
172+
}
173+
174+
if (config.excludeRegExp) {
175+
tree = exclude(tree, config.excludeRegExp);
176+
}
177+
178+
return {
179+
tree: sort(tree),
180+
skipped: nonExistent,
181+
};
182+
}

0 commit comments

Comments
 (0)