jqassistant-plugin / jqassistant-typescript-plugin Goto Github PK
View Code? Open in Web Editor NEW@jQAssistant plugin for TypeScript
Home Page: https://jqassistant.org
License: GNU General Public License v3.0
@jQAssistant plugin for TypeScript
Home Page: https://jqassistant.org
License: GNU General Public License v3.0
Currently, references to locally (in the same module) declared Enums (e.g. let x = MyEnum.A;
) behave correctly.
This means that the variable declaration type and the member value type both would resolve to MyEnum
. The parent value would resolve to typeof MyEnum
.
This behavior isn't present with imported Enums from other modules. Here the TS TypeChecker internal mechansim for FQN resolution is used, leading to a correct variable declaration type, but incorrect member value types (in this case: parent: "...".MyEnum
and member: "...".MyEnum.A
).
To fix this, special treatment of enum values in the member value processor would be necessary, making the code significantly more complicated.
As long as there's no concrete need for it, this issue should be on hold.
In order to model a certain folder structure (like hexagonal architecture in Java) it would be easier to have corresponding directory relation for a ts module.
(:Directory)-[:CONTAINS]->(:TS:Module)
This way it would be easy to mark directories when then containing modules have a certain DEPENDS_ON relation.
Definition of resolvers for different technologies.
Maybe even provided by the user in the form of configuration?
Usage of node.kind
and ts.SyntaxKind
This should improve accuracy and cover more edge cases.
The goal is to use JSON-Schema as an interface between the Java and TypeScript parts of the plugin.
It should be used to simplify testing, prevent missing implementations on either side, and serve as a single source of truth for the data format used internally by the plugin.
Things to consider:
new
expression as ValueCallconst
type parameterssatisfies
keyword_DeepPartialObject
)Hi all π,
when i compare the cardinality of the DEPENDS_ON relationship between two TS:Module
nodes to the
sum of the DEPENDS_ON relationship cardinalities of their elements they are most of the time equal to each other.
Recently, i found some exceptions where i haven't found an explanation for. In those cases, the DEPENDS_ON cardinality between the TS:Module
nodes is lower than the sum of the element DEPENDS_ON cardinalities (details below).
The source code of the example below shows re-exports or some types:
export type {
ColorMapToken,
ColorNeutralMapToken,
CommonMapToken,
FontMapToken,
HeightMapToken,
MapToken,
SizeMapToken,
StyleMapToken,
} from './maps';
Is this related to the re-export? Did i miss something else?
Thanks!
The cardinality of the DEPENDS_ON relationship between two TS:Module
nodes should equal their contained elements and their sum of the DEPENDS_ON relationship cardinalities. I'd expect the following query to have an empty result:
MATCH (source:TS:Module)-[moduleDependency:DEPENDS_ON]->(target:TS:Module)
MATCH (source)-[elementDependency:DEPENDS_ON]->(moduleElement:TS)<-[:EXPORTS]-(target)
WITH source
,target
,moduleDependency.cardinality AS cardinality
,sum(elementDependency.cardinality) AS elementsCardinality
WHERE cardinality < elementsCardinality
RETURN source.globalFqn, target.globalFqn, cardinality, elementsCardinality
In some rare cases, the above query shows some results where the sum of the element dependency cardinalities exceeds the cardinality between their modules.
Remark: The chosen repository contains a huge Typescript project that takes a while to clone, prepare and scan. I couldn't upload the json because it's bigger than GitHubs file size limit of 25MB. The resulting Graph database is about 3 GB big.
Run npm install
Run npx --yes @jqassistant/ts-lce
Get the resulting report from .reports/jqa/ts-output.json
Scan the file with jQAssistant as described in the Usage section of the jqassistant-typescript-plugin
Start the local Neo4j server to see the results in the Browser.
Take the Cypher query from above to find the described cases.
Use the following, extended Cypher query to get more details and the nodes to explore this part of the Graph.
MATCH (source:TS:Module)-[moduleDependency:DEPENDS_ON]->(target:TS:Module)
MATCH (source)-[elementDependency:DEPENDS_ON]->(moduleElement:TS)<-[:EXPORTS]-(target)
WITH source
,target
,moduleDependency.cardinality AS cardinality
,sum(elementDependency.cardinality) AS elementsCardinality
,collect(DISTINCT moduleElement) AS moduleElements
,count(DISTINCT moduleElement) AS numberOfElements
,collect(DISTINCT labels(moduleElement)) AS elementLabels
WHERE cardinality < elementsCardinality
RETURN (elementsCardinality - cardinality) AS cardinalityDifference
,elementLabels
,count(*) AS numberOfCases
,max(numberOfElements) AS maxNumberOfElements
,min(numberOfElements) AS minNumberOfElements
,avg(numberOfElements) AS avgNumberOfElements
,collect(DISTINCT source.globalFqn)[0..4] AS moduleNameExamples
,collect(DISTINCT target.globalFqn)[0..4] AS targetNameExamples
,collect(DISTINCT source)[0..4] AS moduleExamples
,collect(DISTINCT target)[0..4] AS targetExamples
,collect(DISTINCT moduleElements)[0..4] AS elementExamples
ORDER BY cardinalityDifference DESC, numberOfCases DESC
LIMIT 30
Hi π,
i'm getting a lot of error messages when trying to scan react-router (as an example).
Most of them are
Error: external module ... for re-export could not be found: Ignoring export...
andError: Could not resolve module:...
.Details see attached log below. I'm not sure if these messages are ok or not.
Anyway, I suspect missing data in the Graph. For example, I can't find an Interface
node for Location or the (re)exported type of it in the type of it in the index.ts. There is only a ExternalDeclaration
where I couldn't find a way to find out if this is a type, interface or class. Overall, I only get 16 Interfaces. There should be more than 50 if I counted them right in my IDE.
Are there any additional steps I need to take care of before I start the scan? Are only specific project structures supported? Or might this be a bug?
As a software engineer that keep an eye on micro architecture I'm not only interested in dependencies between modules but also on how strong the resulting coupling might be. Calling a function or extending a class from another module leads to more coupling than for example referencing a type alias or an interface.
I want to query the number of interfaces a module uses from another one, the number of types (type aliases), the number of function calls, etc. .This will then also help to calculate a kind of "Abstractness" similar to Object Oriented Design Metrics ( Archived Example ).
yarn install || yarn
npx --yes @jqassistant/ts-lce >jqassostant-typescript-scan.log
If I can provide any additional information or help let me know.
Thanks π very much.
Johannes
Hi π
I've discovered that some ExternalModule
and ExternalDeclaration
nodes have a lower-cased globalFqn
property.
Is that intended or a technical necessity? Or might that be a bug?
There is for example an ExternalModule
with lower-cased globalFqn
= /users/johnny/repositories/git/react-router/packages/react-router/lib/context.ts
. Another one has the original (unchanged) globalFqn
= /Users/johnny/Repositories/git/react-router/packages/router/router.ts
. (see Example Query 1)
Nodes labelled with ExternalModule
and ExternalDeclaration
are the only ones that are affected by this as far as i've seen.
Edit: As it seems this also applies to ExternalType
nodes. Some of them have a lower-cased property ts.referencedGlobalFqn
like "/users/johnny/repositories/git/react-router/packages/react-router/lib/context.ts".RouteObject"
, some not like "/users/johnny/repositories/git/react-router/packages/react-router/lib/context.ts".RouteObject". (see Example Query 2)
MATCH (ts:TS)
WHERE ts.globalFqn IS NOT NULL
RETURN labels(ts)
,split(ts.globalFqn, '/')[1] AS globalRoot
,count(*) as nodeCount
,collect(ts.globalFqn)[0..4] AS exampleGlobalFqns
MATCH (ts:TS)
WITH ts, split(ts.referencedGlobalFqn, '/')[1] AS globalRoot
WHERE globalRoot IS NOT NULL
RETURN labels(ts)
,globalRoot
,count(*) as nodeCount
,collect(ts.referencedGlobalFqn)[0..4] AS exampleReferencedGlobalFqns
Taken from react-router: ts-output-react-router.json
Consideration of:
.cts
file extension)type
field in package.json (irrelevant)moduleSuffixes
compiler option in tsconfig (irrelevant)Execution of Java part via TS command and vice-versa.
This may require a new global strategy for jQA
-> make scanning for projects more versatile
Goals:
provided root folder can contain project(s) in any sub-directory (ignore node_modules)
scanning of multiple projects
TSConfig inheritance and nested projects
differentiation between project inheritance (via extends) and project references (via references)
inheritance
references Docs
how to handle dependencies between scanned projects?
Hey π,
i've recognized that ExternalDeclaration
cardinality isn't included in the sum of cardinalities of the DEPENDS_ON
relationship between a Module
and an ExternalModule
. Everything else that Module
declares or depends on seems to be included as i'd expect.
Here is a picture of an example from the analysis of ts-output-react-router.json with the example-query.cypher:
DEPENDS_ON
relationships are displayed with their cardinalitiesìndex.ts
node is the Module
history.ts
node is the ExternalModule
Module
and depend on ExternalModule
. Their cardinalities sum up to 4 which equals the cardinality between Module
and ExternalModule
.ExternalDeclaration
. They are exported by the ExternalModule
and used within the ExternalModule
. Their cardinalities (2x2=4) between Module
and ExternalDeclaration
are not included in the sum of cardinalities between Module
and ExternalModule
.To rephrase that in cypher language π:
module_rel.cardinality
of (source:TS:Module)-[module_rel:DEPENDS_ON]->(target:TS:ExternalModule)
contains the sum of cardinalities between those moduleselement_rel.cardinality
of (source)-[:DECLARES]->(element)-[element_rel:DEPENDS_ON]->(target)
declaration_rel.cardinality
of (source)-[declaration_rel:DEPENDS_ON]->(declaration:ExternalDeclaration)<-[:EXPORTS]-(target)
What is reason for leaving out external declaration dependencies between modules or is this unintentional?
Thank you very much π,
Johannes
By the way: Awesome plugin for Typescript π. I'm trying to integrate it into my code-graph-analysis-pipeline.
Inspiration:
https://www.perssondennis.com/articles/react-anti-patterns-and-best-practices-dos-and-donts
Features/Concepts needed:
-> General scan of nested functions and instructional code?
Options:
Could be done via jqassistant.yml, but would require execution of TS scanner via Java
Add addition test cases for:
This could be especially interesting for optional, spread, or default value parameters of functions and methods.
It could also be applied to type parameters and type arguments
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.