Giter Site home page Giter Site logo

jqassistant-plugin / jqassistant-typescript-plugin Goto Github PK

View Code? Open in Web Editor NEW
4.0 4.0 2.0 9.09 MB

@jQAssistant plugin for TypeScript

Home Page: https://jqassistant.org

License: GNU General Public License v3.0

Java 27.94% JavaScript 0.21% TypeScript 71.85%
jqassistant plugin scanner typescript

jqassistant-typescript-plugin's People

Contributors

actions-user avatar dependabot[bot] avatar dirkmahler avatar murdos avatar sebastianwendorf avatar stephanpirnbaum avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

deleonio murdos

jqassistant-typescript-plugin's Issues

Fix enum value types (see skipped variable declaration test)

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.

Provide Directory relation for Module

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.

Use JSON-Schema

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.

Missing language features

Things to consider:

  • destructuring assignment with variable declarations
  • parameter destructuring
  • re-exports
  • namespaces
  • function/method overloads
  • async functions/methods
  • class values (assigned to variables)
  • typeof type
  • named tuples
  • this parameter in functions
  • default parameters
  • new expression as ValueCall
  • rest element types and values (Link)
  • computed member expressions
  • new decorator standard
    • decorators may be written before or after export keyword
    • parameter can only be decorated with legacy system (will change in the future)
  • auto accessors
    • initial value of auto accessors
  • const type parameters
  • satisfies keyword
  • index signatures in object types
  • callable types in object types
  • recursive types (e.g. _DeepPartialObject)
  • generator functions
  • conditional types
  • instantiation expressions for generic functions

Should DEPENDS_ON cardinality between Modules equal the sum of their element dependencies?

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!

ModuleCardinalityLowerThanSumOfElements

Expected Behavior

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

Actual behavior

In some rare cases, the above query shows some results where the sum of the element dependency cardinalities exceeds the cardinality between their modules.

Steps to reproduce

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.

  • Clone https://github.com/ant-design/ant-design

  • 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

Missing Interfaces and other elements in the Graph

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... and
  • Error: 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?

Use case

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 ).

Steps to reproduce

If I can provide any additional information or help let me know.

Thanks πŸ™ very much.
Johannes

Lower cased globalFqn property in some ExternalModule and ExternalDeclaration nodes

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)

Example Query

 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

Example Query 2

 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

Example data

Taken from react-router: ts-output-react-router.json

Correct handling of dependencies and module paths/code

  • Node.js resolver -> resolves paths to node_modules to name of package (by reading package.json)

Consideration of:

  • CommonJS code (e.g. .cts file extension)
  • type field in package.json (irrelevant)
  • moduleSuffixes compiler option in tsconfig (irrelevant)
  • Triple-Slash Directives (marked as not supported)

Simplify Usage of Plugin

Execution of Java part via TS command and vice-versa.
This may require a new global strategy for jQA

Dynamic Project Detection

-> 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

    • project root path in project concept is initial tsconfig file, that is scanned
    • FQNs are constructed using common ancestor directory of all inherited configs
  • references Docs

    • would require projectRootPath property on all nodes generated (could easily realized via property on TS Descriptor)
    • result JSON would be array of objects following the already existing format (identified via project concept)
    • fully scanning references projects should be toggleable
  • how to handle dependencies between scanned projects?

Should DEPENDS_ON cardinality between Module and ExternalModule include ExternalDeclarations?

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:

example-graph
  • DEPENDS_ON relationships are displayed with their cardinalities
  • The orange Γ¬ndex.ts node is the Module
  • The grey history.ts node is the ExternalModule
  • The blue and green nodes are elemens that are declared by Module and depend on ExternalModule. Their cardinalities sum up to 4 which equals the cardinality between Module and ExternalModule.
  • The purple nodes are labeled 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 modules
  • The sum above contains all element_rel.cardinality of (source)-[:DECLARES]->(element)-[element_rel:DEPENDS_ON]->(target)
  • The sum above does not contain 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.

Implement Rules/Concepts for React Best Practices

Inspiration:
https://www.perssondennis.com/articles/react-anti-patterns-and-best-practices-dos-and-donts

Features/Concepts needed:

  • React Components (only functional kind for now)
    • variable declarations within component functions (name, type) -> requires destructure assignment support
    • function declarations within component functions (for standard callback function vs. useCallback result)
    • hook calls (with potential custom behavior)
      • useEffect -> register dependencies of callback function to other local variables

-> General scan of nested functions and instructional code?

Configuration of included/ignored directories

Options:

  • explicit inclusion (default: everything in project, or tsconfig if available)
  • explicit exclusion (default: node_modules, or tsconfig if available)

Could be done via jqassistant.yml, but would require execution of TS scanner via Java

Complete Tests

Add addition test cases for:

  • JSX code
  • async functions/methods
  • default imports/exports/re-exports
  • code coordinates -> determine concrete requirements

Linking of argument nodes to parameter nodes

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.