Skip to content
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Label external types and external annotations. Requires 'Label_base_java_types', 'Label_buildin_java_types' and 'Label_resolved_duplicate_types' of 'Types' directory.

MATCH (type:Type&!PrimitiveType&!Void&!JavaType&!ResolvedDuplicateType)
MATCH (type:Type&!PrimitiveType&!Void&!JavaType&!ResolvedDuplicateType&!TS)
WITH type
,type.byteCodeVersion IS NULL AS isExternalType
,exists((type)<-[:OF_TYPE]-()<-[:ANNOTATED_BY]-()) AS isAnnotation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,66 @@
// Inspired by https://github.com/jQAssistant/jqassistant/blob/4cd7face5d6d2953449d8e6ff5b484f00ffbdc2f/plugin/java/src/main/resources/META-INF/jqassistant-rules/java-classpath.xml#L5
// Related to https://github.com/jqassistant-plugin/jqassistant-typescript-plugin/issues/35

MATCH (module:TS:Module)
WHERE module.globalFqn IS NOT NULL
MATCH (module:TS:Module)<-[:CONTAINS]-(package:TS:Project)
WHERE module.globalFqn IS NOT NULL
AND EXISTS { (module)-[:EXPORTS]->(:TS) } // only when module exports something
MATCH (externalModule:TS:ExternalModule)
WHERE module.globalFqn IS NOT NULL
AND ((module.globalFqn = externalModule.globalFqn)
OR (module.module = externalModule.module)
OR ( externalModule.name = module.name
AND externalModule.moduleName = module.moduleName
AND externalModule.namespace = module.namespace
AND externalModule.extensionExtended = module.extensionExtended
AND externalModule.globalFqn ENDS WITH module.localModulePath
)
)
AND module <> externalModule
AND externalModule <> module
AND externalModule.name = module.name // Base requirement: Same module name
AND EXISTS { (externalModule)-[:EXPORTS]->(:TS:ExternalDeclaration)<-[]-(used:TS) } // only when external declarations are used
WITH *, size(externalModule.extensionExtended) AS externalExtensionSize
WITH *, CASE externalModule.extensionExtended
WHEN ENDS WITH '.js' THEN left(externalModule.extensionExtended, externalExtensionSize - 3) + module.extension
WHEN ENDS WITH 'd.ts' THEN left(externalModule.extensionExtended, externalExtensionSize - 4) + module.extension
ELSE externalModule.extensionExtended
END AS normalizedExternalExtension
WITH *
// Find internal and external modules with identical "globalFqn"
,(module.globalFqn = externalModule.globalFqn) AS equalGlobalFqn
// Find internal and external modules with identical "module"
,(module.module = externalModule.module) AS equalModule
// Find matching internal and external modules within the same package and namespace
,( externalModule.namespace > ''
AND externalModule.namespace = module.namespace
AND externalModule.packageName = package.name
AND normalizedExternalExtension = module.extensionExtended
AND externalModule.globalFqn ENDS WITH module.localModulePath
) AS equalNameAndNamespace
// Find matching internal and external module without a namespace with matching local module path
,( module.namespace = ''
AND externalModule.namespace = ''
AND normalizedExternalExtension = module.extensionExtended
AND externalModule.globalFqn ENDS WITH
replace(module.localModulePath, '/' + module.moduleName, '/' + externalModule.moduleName)
) AS equalNameWithoutNamespace
// Find matching module name, npm package name and npm package namespace
,( externalModule.namespace = module.namespace
AND externalModule.packageName = module.packageName
AND externalModule.packageName > ''
AND normalizedExternalExtension = module.extensionExtended
) AS equalNameAndNpmPackage
WHERE equalGlobalFqn
OR equalModule
OR equalNameAndNamespace
OR equalNameWithoutNamespace
OR equalNameAndNpmPackage
CALL { WITH module, externalModule
MERGE (externalModule)-[:RESOLVES_TO]->(module)
} IN TRANSACTIONS
RETURN count(*) AS resolvedModules
RETURN CASE WHEN equalGlobalFqn THEN 'equalGlobalFqn'
WHEN equalModule THEN 'equalModule'
WHEN equalNameWithoutNamespace THEN 'equalNameWithoutNamespace'
WHEN equalNameAndNamespace THEN 'equalNameAndNamespace'
WHEN equalNameAndNpmPackage THEN 'equalNameAndNpmPackage'
END AS equality
,count(*) AS resolvedModules
,collect(externalModule.globalFqn + ' -> ' + module.globalFqn)[0..4] AS examples
// Debugging
// RETURN module.globalFqn ,externalModule.globalFqn
// ,module.name ,externalModule.name
// ,module.moduleName ,externalModule.moduleName
// ,module.namespace ,externalModule.namespace
// ,module.extensionExtended, externalModule.extensionExtended
// ,module.localModulePath
// ,split(module.module, '/')[-2] AS moduleDirectory
4 changes: 3 additions & 1 deletion cypher/Typescript_Enrichment/Add_module_properties.cypher
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ OPTIONAL MATCH (class:TS:Class)-[:DECLARES]->(ts)
,nullif(reverse(split(reverse(moduleName), '.')[0]), moduleName) AS moduleNameExtension
,coalesce('@' + nullif(namespaceName, ''), '') AS namespaceNameWithAtPrefixed
,replace(symbolName, coalesce(optionalClassName + '.', ''), '') AS symbolNameWithoutClassName
SET ts.namespace = namespaceNameWithAtPrefixed
,coalesce(split(split(ts.globalFqn, nullif(namespaceName, '') + '/')[1], '/')[0], '') AS packageName
SET ts.namespace = coalesce(nullif(namespaceNameWithAtPrefixed, ''), ts.namespace, '')
,ts.module = modulePathNameWithoutIndexAndDefault
,ts.moduleName = moduleName
,ts.name = coalesce(symbolNameWithoutClassName, indexAndExtensionOmittedName)
Expand All @@ -26,6 +27,7 @@ OPTIONAL MATCH (class:TS:Class)-[:DECLARES]->(ts)
,ts.isNodeModule = isNodeModule
,ts.isUnresolvedImport = isUnresolvedImport
,ts.isExternalImport = isNodeModule OR isUnresolvedImport
,ts.packageName = packageName
RETURN count(*) AS updatedModules
// For debugging
// RETURN namespaceNameWithAtPrefixed AS namespace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
MATCH (project:TS:Project)-[:HAS_ROOT]->(root:Directory)
OPTIONAL MATCH (project)-[:HAS_CONFIG]->(config:File)<-[:CONTAINS]-(config_dir:Directory)
WITH *
,reverse(split(reverse(root.absoluteFileName), '/')[0]) AS projectNameFromRoot
,reverse(split(reverse(config_dir.absoluteFileName), '/')[0]) AS projectNameFromConfig
,reverse(split(reverse(root.absoluteFileName), '/')[0]) AS projectNameFromRoot
,reverse(split(reverse(config_dir.absoluteFileName), '/')[0]) AS projectNameFromConfig
,nullif(substring(replace(config.name, 'tsconfig', ''), 1), '') AS projectAddOnFromConfig
WITH *
,projectNameFromRoot + '/' +
nullif(projectNameFromConfig, projectNameFromRoot) AS projectNameWithDifferentConfigIfPresent
nullif(projectNameFromConfig, projectNameFromRoot) +
coalesce(' (' + projectAddOnFromConfig + ')', '') AS projectNameWithDifferentConfigIfPresent
WITH *
,coalesce(projectNameWithDifferentConfigIfPresent, projectNameFromRoot) AS projectName
SET project.name = projectName
RETURN count(*) AS numberOfNamesProjects
// For debugging
//RETURN projectNameFromRoot
// ,projectNameFromConfig
// ,projectNameWithDifferentConfig
// ,projectNameWithDifferentConfigIfPresent
// ,projectName
// ,project, root, config
//LIMIT 10
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Add namespace property to Typescript nodes if a npm a package is linked. Requires Link_projects_to_npm_packages.

MATCH (element:TS)<-[:CONTAINS]-(project:TS:Project)-[:HAS_NPM_PACKAGE]->(package:NPM:Package)
WHERE element.globalFqn IS NOT NULL
WITH *
,coalesce(nullif(split(package.name, '/')[0], package.name), '') AS npmPackageNamespace
,coalesce(split(package.name, '/')[1], package.name) AS packageName
SET element.namespace = coalesce(nullif(element.namespace, ''), npmPackageNamespace)
SET element.packageName = coalesce(nullif(element.packageName, ''), packageName)
RETURN labels(element)[0..4] AS nodeLabels, count(*) AS numberOfWrittenNamespaceProperties
// Debugging
//RETURN element.globalFqn, element.moduleName, element.namespace
// ,package.name, package.fileName
// ,npmPackageNamespace
// ,packageName
//LIMIT 10
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Link npm dependencies to the npm package that describe them if it exists

MATCH (npm_dependency:NPM:Dependency)
MATCH (npm_package:NPM:Package)
WHERE npm_package.name = npm_dependency.name
AND npm_package <> npm_dependency
CALL { WITH npm_package, npm_dependency
MERGE (npm_dependency)-[:IS_DESCRIBED_IN_NPM_PACKAGE]->(npm_package)
} IN TRANSACTIONS
RETURN count(*) AS numberOfWrittenRelationships
,count(DISTINCT npm_dependency) AS numberOfDistinctNpmDependencies
,count(DISTINCT npm_package) AS numberOfDistinctNpmPackages
// Debugging
// RETURN npm_dependency, npm_package
// LIMIT 1
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ MATCH (npmPackage:NPM:Package)
MERGE (project)-[:HAS_NPM_PACKAGE]->(npmPackage)
// Set the "relativeFileDirectory" on the npm package to the relative directory
// that contains the package.json file
SET npmPackage.relativeFileDirectory = relativeNpmPackageDirectory
SET npmPackage.relativeFileDirectory = ltrim(relativeNpmPackageDirectory, '/')
,project.version = npmPackage.version
RETURN count(*) AS numberOfCreatedNpmPackageRelationships
// Detailed results for debugging
Expand Down
12 changes: 7 additions & 5 deletions scripts/prepareAnalysis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,11 @@ execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Mark_test_modules.cypher"
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_name_to_property_on_projects.cypher"
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_name_to_property_on_scan_nodes.cypher"

# Preparation - Enrich Graph for Typescript by adding relationships between Modules with the same globalFqn
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_RESOLVES_TO_relationship_for_matching_modules.cypher"
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_RESOLVES_TO_relationship_for_matching_declarations.cypher"
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_DEPENDS_ON_relationship_to_resolved_modules.cypher"

# Preparation - Cleanup Graph for Typescript by removing duplicate relationships
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Remove_duplicate_CONTAINS_relations_between_files.cypher"

# Preparation - Enrich Graph for Typescript by adding relationships between corresponding TS:Project and NPM:Package nodes
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Link_npm_dependencies_to_npm_packages.cypher"
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Link_projects_to_npm_packages.cypher"
dataVerificationResult=$( execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Verify_projects_linked_to_npm_packages.cypher" "${@}")
if is_csv_column_greater_zero "${dataVerificationResult}" "unresolvedProjectsCount"; then
Expand All @@ -91,6 +87,12 @@ if is_csv_column_greater_zero "${dataVerificationResult}" "unresolvedProjectsCou
# Since this is now only a warning, execution will be continued.
fi
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Link_external_modules_to_corresponding_npm_dependency.cypher"
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_namespace_property_on_nodes_from_linked_npm_packages.cypher"

# Preparation - Enrich Graph for Typescript by adding relationships between Modules with the same globalFqn
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_RESOLVES_TO_relationship_for_matching_modules.cypher"
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_RESOLVES_TO_relationship_for_matching_declarations.cypher"
execute_cypher "${TYPESCRIPT_CYPHER_DIR}/Add_DEPENDS_ON_relationship_to_resolved_modules.cypher"

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