dependency-cruiser
dependency-cruiser copied to clipboard
Question: Ignore/Skip Re-Exports?
Summary
Is there any way to ignore or skip re-exports?
Context
I have some kind of mono repository, based on pnpm workspaces, the hierarchy looks like this
monorepo/
packages/
ui-controls/
src/index.ts
core-modules/
src/index.ts
projects/
website/
admin-panel/
mobile-app/
I try to cruise file from one of my projects, like header.tsx
import { UserInfo, Button } from '@monorepo/ui-controls';
import { login } from '@monorepo/core-modules';
export const Header = funcrion Header(props) {
return (
<div class='header'>
{
props.username
? (<UserInfo username={props.username} />)
: (<Button onClick={login} /> )
}
</div>
);
};
$ /monorepo/projects/website/: depcruise "./src/components/header.tsx" -T json -f depcru.json --config .dependency-cruiser.js
And because I use index.ts
in packages for re-export, i see some kind of this in "dependencies" section:
{
"dynamic": false,
"module": "@monorepo/ui-controls",
"moduleSystem": "es6",
"exoticallyRequired": false,
"resolved": "packages/ui-controls/src/index.ts",
"coreModule": false,
"followable": true,
"couldNotResolve": false,
"dependencyTypes": [
"npm"
],
"matchesDoNotFollow": false,
"circular": false,
"valid": true
},
And below of course I can already see the dependencies of index.ts
, but what I really need is to know that my header depends on the UserInfo
, Button
and login
, and not from the whole package
Environment
- Version used: 11.11.0
- Node version: v16.13.2
- Operating System and version: macOS Monterey 12.4
@belyanskii do I understand your question correctly you want this situation:
... (where the index.ts
's are 'barrel' files with no functionality, re-exporting everything they import) to be represented like this?:
Technically this is not impossible, but header.tsx
does rely on the index.ts
's and not on the modules it shields as an interface.
What is the reason the code in your example doesn't directly import UserInfo, Button, login and relies on an intermediate index.ts instead?
@sverweij Yes, you understood me correctly!
index.ts
we use because it's just more convenient :(
U don't need to set whole path to component/module/helper, just use re-export and all the imports in the project become the same. The team developing the package gives the API, and where the files are located inside is not so important
Why do I need this functionality at all?
When i edit file, like button.ts
in my example, our Ci-machinery run builds and tests of whole code of all packages and projects, and this is a little too much )
My task is to build a graph to understand exactly which files depend on the button.ts
and decide which tests and builds to run.
If dependency-cruiser could ignore re-exports, it would be pretty easy, just cruise whole project and take all sources with buttin.ts
in deps.
What other option do I see?
When dependency-cruiser read imports, they can make verbose variant of result and show AST parsed imports, like this
// take some .ts file and get AST
const node = ts.createSourceFile(
'x.ts',
fs.readFileSync('button.ts', 'utf8'),
ts.ScriptTarget.Latest
);
const importDecl = {};
node.forEachChild(child => {
if (ts.SyntaxKind[child.kind] === 'ImportDeclaration') {
const clause = [];
if (child.importClause.namedBindings) {
child.importClause.namedBindings.elements.map(
el => clause.push(el.name.escapedText)
);
} else {
clause.push(child.importClause.name.escapedText);
}
importDecl[child.moduleSpecifier.text] = clause;
}
});
It's old example, but idea is to get:
{
'@monorepo/ui-controls': ['Button', 'UserInfo'],
'@monorepo/core-modules': ['login']
}
Here we do not ignore re-exports and do not go deeper, but simply supplement the knowledge that is imported from the file, and if it were possible, then we would get all the same, but with additional information
{
"dynamic": false,
"module": "@monorepo/ui-controls",
"moduleSystem": "es6",
"resolvedImports": ["Button", "UserInfo"], // like this o_O
"exoticallyRequired": false,
"resolved": "packages/ui-controls/src/index.ts",
"coreModule": false,
"followable": true,
"couldNotResolve": false,
"dependencyTypes": [
"npm"
],
"matchesDoNotFollow": false,
"circular": false,
"valid": true
},
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
whoops - I've missed a few stale notifications - re-open please 😬
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
I stumbled upon this package this week, and this is the main blocker for me using it effectively. I am not familiar with the codebase, but I am willing to help contribute for this functionality.
@belyanskii do I understand your question correctly you want this situation:我是否正确理解你的问题你想要这种情况:
... (where the
index.ts
's are 'barrel' files with no functionality, re-exporting everything they import) to be represented like this?:...(其中index.ts
是没有功能的“桶”文件,重新导出它们导入的所有内容)像这样表示?:
Technically this is not impossible, but
header.tsx
does rely on theindex.ts
's and not on the modules it shields as an interface.从技术上讲,这并非不可能,但header.tsx
确实依赖于index.ts
,而不是它作为接口屏蔽的模块。What is the reason the code in your example doesn't directly import UserInfo, Button, login and relies on an intermediate index.ts instead?您的示例中的代码不直接导入 UserInfo、Button、login 而是依赖中间的 index.ts 的原因是什么?
Is there any update on this issue? I'm experiencing a similar need and would greatly appreciate any progress made.