Skip to content

Commit a655b01

Browse files
authored
Merge pull request #248 from JohT/feature/improve-external-to-internal-typescript-module-links
Improve RESOLVES_TO relationships between external and internal Typescript modules
2 parents b37f18e + 85bc48a commit a655b01

9 files changed

+182
-68
lines changed

cypher/External_Dependencies/Label_external_types_and_annotations.cypher

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Label external types and external annotations. Requires 'Label_base_java_types', 'Label_buildin_java_types' and 'Label_resolved_duplicate_types' of 'Types' directory.
22

3-
MATCH (type:Type&!PrimitiveType&!Void&!JavaType&!ResolvedDuplicateType)
3+
MATCH (type:Type&!PrimitiveType&!Void&!JavaType&!ResolvedDuplicateType&!TS)
44
WITH type
55
,type.byteCodeVersion IS NULL AS isExternalType
66
,exists((type)<-[:OF_TYPE]-()<-[:ANNOTATED_BY]-()) AS isAnnotation

cypher/Typescript_Enrichment/Add_RESOLVES_TO_relationship_for_matching_modules.cypher

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,66 @@
33
// Inspired by https://github.com/jQAssistant/jqassistant/blob/4cd7face5d6d2953449d8e6ff5b484f00ffbdc2f/plugin/java/src/main/resources/META-INF/jqassistant-rules/java-classpath.xml#L5
44
// Related to https://github.com/jqassistant-plugin/jqassistant-typescript-plugin/issues/35
55

6-
MATCH (module:TS:Module)
7-
WHERE module.globalFqn IS NOT NULL
6+
MATCH (module:TS:Module)<-[:CONTAINS]-(package:TS:Project)
7+
WHERE module.globalFqn IS NOT NULL
8+
AND EXISTS { (module)-[:EXPORTS]->(:TS) } // only when module exports something
89
MATCH (externalModule:TS:ExternalModule)
910
WHERE module.globalFqn IS NOT NULL
10-
AND ((module.globalFqn = externalModule.globalFqn)
11-
OR (module.module = externalModule.module)
12-
OR ( externalModule.name = module.name
13-
AND externalModule.moduleName = module.moduleName
14-
AND externalModule.namespace = module.namespace
15-
AND externalModule.extensionExtended = module.extensionExtended
16-
AND externalModule.globalFqn ENDS WITH module.localModulePath
17-
)
18-
)
19-
AND module <> externalModule
11+
AND externalModule <> module
12+
AND externalModule.name = module.name // Base requirement: Same module name
13+
AND EXISTS { (externalModule)-[:EXPORTS]->(:TS:ExternalDeclaration)<-[]-(used:TS) } // only when external declarations are used
14+
WITH *, size(externalModule.extensionExtended) AS externalExtensionSize
15+
WITH *, CASE externalModule.extensionExtended
16+
WHEN ENDS WITH '.js' THEN left(externalModule.extensionExtended, externalExtensionSize - 3) + module.extension
17+
WHEN ENDS WITH 'd.ts' THEN left(externalModule.extensionExtended, externalExtensionSize - 4) + module.extension
18+
ELSE externalModule.extensionExtended
19+
END AS normalizedExternalExtension
20+
WITH *
21+
// Find internal and external modules with identical "globalFqn"
22+
,(module.globalFqn = externalModule.globalFqn) AS equalGlobalFqn
23+
// Find internal and external modules with identical "module"
24+
,(module.module = externalModule.module) AS equalModule
25+
// Find matching internal and external modules within the same package and namespace
26+
,( externalModule.namespace > ''
27+
AND externalModule.namespace = module.namespace
28+
AND externalModule.packageName = package.name
29+
AND normalizedExternalExtension = module.extensionExtended
30+
AND externalModule.globalFqn ENDS WITH module.localModulePath
31+
) AS equalNameAndNamespace
32+
// Find matching internal and external module without a namespace with matching local module path
33+
,( module.namespace = ''
34+
AND externalModule.namespace = ''
35+
AND normalizedExternalExtension = module.extensionExtended
36+
AND externalModule.globalFqn ENDS WITH
37+
replace(module.localModulePath, '/' + module.moduleName, '/' + externalModule.moduleName)
38+
) AS equalNameWithoutNamespace
39+
// Find matching module name, npm package name and npm package namespace
40+
,( externalModule.namespace = module.namespace
41+
AND externalModule.packageName = module.packageName
42+
AND externalModule.packageName > ''
43+
AND normalizedExternalExtension = module.extensionExtended
44+
) AS equalNameAndNpmPackage
45+
WHERE equalGlobalFqn
46+
OR equalModule
47+
OR equalNameAndNamespace
48+
OR equalNameWithoutNamespace
49+
OR equalNameAndNpmPackage
2050
CALL { WITH module, externalModule
2151
MERGE (externalModule)-[:RESOLVES_TO]->(module)
2252
} IN TRANSACTIONS
23-
RETURN count(*) AS resolvedModules
53+
RETURN CASE WHEN equalGlobalFqn THEN 'equalGlobalFqn'
54+
WHEN equalModule THEN 'equalModule'
55+
WHEN equalNameWithoutNamespace THEN 'equalNameWithoutNamespace'
56+
WHEN equalNameAndNamespace THEN 'equalNameAndNamespace'
57+
WHEN equalNameAndNpmPackage THEN 'equalNameAndNpmPackage'
58+
END AS equality
59+
,count(*) AS resolvedModules
60+
,collect(externalModule.globalFqn + ' -> ' + module.globalFqn)[0..4] AS examples
61+
// Debugging
62+
// RETURN module.globalFqn ,externalModule.globalFqn
63+
// ,module.name ,externalModule.name
64+
// ,module.moduleName ,externalModule.moduleName
65+
// ,module.namespace ,externalModule.namespace
66+
// ,module.extensionExtended, externalModule.extensionExtended
67+
// ,module.localModulePath
68+
// ,split(module.module, '/')[-2] AS moduleDirectory

cypher/Typescript_Enrichment/Add_module_properties.cypher

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ OPTIONAL MATCH (class:TS:Class)-[:DECLARES]->(ts)
1717
,nullif(reverse(split(reverse(moduleName), '.')[0]), moduleName) AS moduleNameExtension
1818
,coalesce('@' + nullif(namespaceName, ''), '') AS namespaceNameWithAtPrefixed
1919
,replace(symbolName, coalesce(optionalClassName + '.', ''), '') AS symbolNameWithoutClassName
20-
SET ts.namespace = namespaceNameWithAtPrefixed
20+
,coalesce(split(split(ts.globalFqn, nullif(namespaceName, '') + '/')[1], '/')[0], '') AS packageName
21+
SET ts.namespace = coalesce(nullif(namespaceNameWithAtPrefixed, ''), ts.namespace, '')
2122
,ts.module = modulePathNameWithoutIndexAndDefault
2223
,ts.moduleName = moduleName
2324
,ts.name = coalesce(symbolNameWithoutClassName, indexAndExtensionOmittedName)
@@ -26,6 +27,7 @@ OPTIONAL MATCH (class:TS:Class)-[:DECLARES]->(ts)
2627
,ts.isNodeModule = isNodeModule
2728
,ts.isUnresolvedImport = isUnresolvedImport
2829
,ts.isExternalImport = isNodeModule OR isUnresolvedImport
30+
,ts.packageName = packageName
2931
RETURN count(*) AS updatedModules
3032
// For debugging
3133
// RETURN namespaceNameWithAtPrefixed AS namespace

cypher/Typescript_Enrichment/Add_name_to_property_on_projects.cypher

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@
33
MATCH (project:TS:Project)-[:HAS_ROOT]->(root:Directory)
44
OPTIONAL MATCH (project)-[:HAS_CONFIG]->(config:File)<-[:CONTAINS]-(config_dir:Directory)
55
WITH *
6-
,reverse(split(reverse(root.absoluteFileName), '/')[0]) AS projectNameFromRoot
7-
,reverse(split(reverse(config_dir.absoluteFileName), '/')[0]) AS projectNameFromConfig
6+
,reverse(split(reverse(root.absoluteFileName), '/')[0]) AS projectNameFromRoot
7+
,reverse(split(reverse(config_dir.absoluteFileName), '/')[0]) AS projectNameFromConfig
8+
,nullif(substring(replace(config.name, 'tsconfig', ''), 1), '') AS projectAddOnFromConfig
89
WITH *
910
,projectNameFromRoot + '/' +
10-
nullif(projectNameFromConfig, projectNameFromRoot) AS projectNameWithDifferentConfigIfPresent
11+
nullif(projectNameFromConfig, projectNameFromRoot) +
12+
coalesce(' (' + projectAddOnFromConfig + ')', '') AS projectNameWithDifferentConfigIfPresent
1113
WITH *
1214
,coalesce(projectNameWithDifferentConfigIfPresent, projectNameFromRoot) AS projectName
1315
SET project.name = projectName
1416
RETURN count(*) AS numberOfNamesProjects
1517
// For debugging
1618
//RETURN projectNameFromRoot
1719
// ,projectNameFromConfig
18-
// ,projectNameWithDifferentConfig
20+
// ,projectNameWithDifferentConfigIfPresent
1921
// ,projectName
2022
// ,project, root, config
2123
//LIMIT 10
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Add namespace property to Typescript nodes if a npm a package is linked. Requires Link_projects_to_npm_packages.
2+
3+
MATCH (element:TS)<-[:CONTAINS]-(project:TS:Project)-[:HAS_NPM_PACKAGE]->(package:NPM:Package)
4+
WHERE element.globalFqn IS NOT NULL
5+
WITH *
6+
,coalesce(nullif(split(package.name, '/')[0], package.name), '') AS npmPackageNamespace
7+
,coalesce(split(package.name, '/')[1], package.name) AS packageName
8+
SET element.namespace = coalesce(nullif(element.namespace, ''), npmPackageNamespace)
9+
SET element.packageName = coalesce(nullif(element.packageName, ''), packageName)
10+
RETURN labels(element)[0..4] AS nodeLabels, count(*) AS numberOfWrittenNamespaceProperties
11+
// Debugging
12+
//RETURN element.globalFqn, element.moduleName, element.namespace
13+
// ,package.name, package.fileName
14+
// ,npmPackageNamespace
15+
// ,packageName
16+
//LIMIT 10
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Link npm dependencies to the npm package that describe them if it exists
2+
3+
MATCH (npm_dependency:NPM:Dependency)
4+
MATCH (npm_package:NPM:Package)
5+
WHERE npm_package.name = npm_dependency.name
6+
AND npm_package <> npm_dependency
7+
CALL { WITH npm_package, npm_dependency
8+
MERGE (npm_dependency)-[:IS_DESCRIBED_IN_NPM_PACKAGE]->(npm_package)
9+
} IN TRANSACTIONS
10+
RETURN count(*) AS numberOfWrittenRelationships
11+
,count(DISTINCT npm_dependency) AS numberOfDistinctNpmDependencies
12+
,count(DISTINCT npm_package) AS numberOfDistinctNpmPackages
13+
// Debugging
14+
// RETURN npm_dependency, npm_package
15+
// LIMIT 1

cypher/Typescript_Enrichment/Link_projects_to_npm_packages.cypher

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ MATCH (npmPackage:NPM:Package)
2424
MERGE (project)-[:HAS_NPM_PACKAGE]->(npmPackage)
2525
// Set the "relativeFileDirectory" on the npm package to the relative directory
2626
// that contains the package.json file
27-
SET npmPackage.relativeFileDirectory = relativeNpmPackageDirectory
27+
SET npmPackage.relativeFileDirectory = ltrim(relativeNpmPackageDirectory, '/')
2828
,project.version = npmPackage.version
2929
RETURN count(*) AS numberOfCreatedNpmPackageRelationships
3030
// Detailed results for debugging

scripts/prepareAnalysis.sh

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,11 @@ execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Mark_test_modules.cypher"
7272
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_name_to_property_on_projects.cypher"
7373
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_name_to_property_on_scan_nodes.cypher"
7474

75-
# Preparation - Enrich Graph for Typescript by adding relationships between Modules with the same globalFqn
76-
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_RESOLVES_TO_relationship_for_matching_modules.cypher"
77-
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_RESOLVES_TO_relationship_for_matching_declarations.cypher"
78-
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_DEPENDS_ON_relationship_to_resolved_modules.cypher"
79-
8075
# Preparation - Cleanup Graph for Typescript by removing duplicate relationships
8176
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Remove_duplicate_CONTAINS_relations_between_files.cypher"
8277

8378
# Preparation - Enrich Graph for Typescript by adding relationships between corresponding TS:Project and NPM:Package nodes
79+
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Link_npm_dependencies_to_npm_packages.cypher"
8480
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Link_projects_to_npm_packages.cypher"
8581
dataVerificationResult=$( execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Verify_projects_linked_to_npm_packages.cypher" "${@}")
8682
if is_csv_column_greater_zero "${dataVerificationResult}" "unresolvedProjectsCount"; then
@@ -91,6 +87,12 @@ if is_csv_column_greater_zero "${dataVerificationResult}" "unresolvedProjectsCou
9187
# Since this is now only a warning, execution will be continued.
9288
fi
9389
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Link_external_modules_to_corresponding_npm_dependency.cypher"
90+
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_namespace_property_on_nodes_from_linked_npm_packages.cypher"
91+
92+
# Preparation - Enrich Graph for Typescript by adding relationships between Modules with the same globalFqn
93+
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_RESOLVES_TO_relationship_for_matching_modules.cypher"
94+
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_RESOLVES_TO_relationship_for_matching_declarations.cypher"
95+
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_DEPENDS_ON_relationship_to_resolved_modules.cypher"
9496

9597
# Preparation - Add weights to Java Package DEPENDS_ON relationships
9698
execute_cypher_summarized "${DEPENDS_ON_CYPHER_DIR}/Add_weight_property_for_Java_Interface_Dependencies_to_Package_DEPENDS_ON_Relationship.cypher"

0 commit comments

Comments
 (0)