diff --git a/analysis/analysers/parsers/UnifiedParser/README.md b/analysis/analysers/parsers/UnifiedParser/README.md index cd33a39813..2571dd1204 100644 --- a/analysis/analysers/parsers/UnifiedParser/README.md +++ b/analysis/analysers/parsers/UnifiedParser/README.md @@ -10,6 +10,7 @@ The Unified Parser is parser to generate code metrics from a source code file or |--------------|----------------------------------------| | Javascript | .js, .cjs, .mjs | | Typescript | .ts, .cts, .mts | +| TSX | .tsx | | Java | .java | | Kotlin | .kt | | C# | .cs | @@ -22,6 +23,7 @@ The Unified Parser is parser to generate code metrics from a source code file or | Ruby | .rb | | Swift | .swift | | Bash | .sh | +| Vue | .vue | ## Supported Metrics diff --git a/analysis/analysers/parsers/UnifiedParser/build.gradle.kts b/analysis/analysers/parsers/UnifiedParser/build.gradle.kts index e0511d0295..cfe57de0db 100644 --- a/analysis/analysers/parsers/UnifiedParser/build.gradle.kts +++ b/analysis/analysers/parsers/UnifiedParser/build.gradle.kts @@ -9,7 +9,7 @@ dependencies { implementation(libs.kotter.test) // TreesitterLibrary provides all TreeSitter dependencies and metric calculation - implementation("com.github.MaibornWolff:TreeSitterExcavationSite:v0.2.0") + implementation("com.github.MaibornWolff:TreeSitterExcavationSite:v0.4.1") testImplementation(libs.jsonassert) } diff --git a/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/AttributeDescriptors.kt b/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/AttributeDescriptors.kt index c094f8583f..dcef105db5 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/AttributeDescriptors.kt +++ b/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/AttributeDescriptors.kt @@ -94,7 +94,10 @@ internal fun getAttributeDescriptors(): Map { ), "mean_complexity_per_function" to AttributeDescriptor( title = "Mean complexity per function", - description = "The mean complexity found in the body of a function of this file." + description = "The mean complexity found in the body of a function of this file.", + link = ghLink, + direction = -1, + analyzers = analyzerName ), "median_complexity_per_function" to AttributeDescriptor( title = "Median complexity per function", diff --git a/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/AvailableCollectors.kt b/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/AvailableCollectors.kt index d039363e80..e6bb56396a 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/AvailableCollectors.kt +++ b/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/AvailableCollectors.kt @@ -3,8 +3,15 @@ package de.maibornwolff.codecharta.analysers.parsers.unified.metriccollectors import de.maibornwolff.codecharta.serialization.FileExtension import de.maibornwolff.treesitter.excavationsite.api.Language -enum class AvailableCollectors(val fileExtension: FileExtension, val collectorFactory: () -> TreeSitterLibraryCollector) { +/** Maps each supported [FileExtension] to a factory for the corresponding [TreeSitterLibraryCollector]. */ +enum class AvailableCollectors( + /** The file extension this collector handles. */ + val fileExtension: FileExtension, + /** Factory function that creates the collector for this language. */ + val collectorFactory: () -> TreeSitterLibraryCollector +) { TYPESCRIPT(FileExtension.TYPESCRIPT, { TreeSitterLibraryCollector(Language.TYPESCRIPT) }), + TSX(FileExtension.TSX, { TreeSitterLibraryCollector(Language.TSX) }), JAVASCRIPT(FileExtension.JAVASCRIPT, { TreeSitterLibraryCollector(Language.JAVASCRIPT) }), KOTLIN(FileExtension.KOTLIN, { TreeSitterLibraryCollector(Language.KOTLIN) }), OBJECTIVE_C(FileExtension.OBJECTIVE_C, { TreeSitterLibraryCollector(Language.OBJECTIVE_C) }), diff --git a/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TreeSitterAdapter.kt b/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TreeSitterAdapter.kt index 5b408e29c7..1dd9def2ea 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TreeSitterAdapter.kt +++ b/analysis/analysers/parsers/UnifiedParser/src/main/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TreeSitterAdapter.kt @@ -51,6 +51,7 @@ object TreeSitterAdapter { FileExtension.JAVA -> Language.JAVA FileExtension.KOTLIN -> Language.KOTLIN FileExtension.TYPESCRIPT -> Language.TYPESCRIPT + FileExtension.TSX -> Language.TSX FileExtension.JAVASCRIPT -> Language.JAVASCRIPT FileExtension.PYTHON -> Language.PYTHON FileExtension.GO -> Language.GO diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/UnifiedParserTest.kt b/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/UnifiedParserTest.kt index 0d8a3807ad..1f580cd2e4 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/UnifiedParserTest.kt +++ b/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/UnifiedParserTest.kt @@ -43,6 +43,7 @@ class UnifiedParserTest { Arguments.of("ruby", ".rb"), Arguments.of("swift", ".swift"), Arguments.of("typescript", ".ts"), + Arguments.of("tsx", ".tsx"), Arguments.of("vue", ".vue") ) } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TreeSitterAdapterTest.kt b/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TreeSitterAdapterTest.kt index 03deeb949f..95d64f1e93 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TreeSitterAdapterTest.kt +++ b/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TreeSitterAdapterTest.kt @@ -64,6 +64,7 @@ class TreeSitterAdapterTest { FileExtension.JAVA to Language.JAVA, FileExtension.KOTLIN to Language.KOTLIN, FileExtension.TYPESCRIPT to Language.TYPESCRIPT, + FileExtension.TSX to Language.TSX, FileExtension.JAVASCRIPT to Language.JAVASCRIPT, FileExtension.PYTHON to Language.PYTHON, FileExtension.GO to Language.GO, @@ -143,6 +144,7 @@ class TreeSitterAdapterTest { FileExtension.JAVA, FileExtension.KOTLIN, FileExtension.TYPESCRIPT, + FileExtension.TSX, FileExtension.JAVASCRIPT, FileExtension.PYTHON, FileExtension.GO, diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TsxCollectorTest.kt b/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TsxCollectorTest.kt new file mode 100644 index 0000000000..904c576c8c --- /dev/null +++ b/analysis/analysers/parsers/UnifiedParser/src/test/kotlin/de/maibornwolff/codecharta/analysers/parsers/unified/metriccollectors/TsxCollectorTest.kt @@ -0,0 +1,36 @@ +package de.maibornwolff.codecharta.analysers.parsers.unified.metriccollectors + +import de.maibornwolff.treesitter.excavationsite.api.AvailableFileMetrics +import de.maibornwolff.treesitter.excavationsite.api.Language +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.io.File + +class TsxCollectorTest { + private val collector = TreeSitterLibraryCollector(Language.TSX) + + private fun createTestFile(content: String): File { + val tempFile = File.createTempFile("testFile", ".tsx") + tempFile.writeText(content) + tempFile.deleteOnExit() + return tempFile + } + + @Test + fun `should parse a minimal tsx component and return non-zero loc`() { + // Arrange + val fileContent = """ + function Hello() { + return
Hello World
; + } + """.trimIndent() + val input = createTestFile(fileContent) + + // Act + val result = collector.collectMetricsForFile(input) + + // Assert + assertThat(result.attributes[AvailableFileMetrics.LINES_OF_CODE.metricName] as Double).isGreaterThan(0.0) + assertThat(result.attributes[AvailableFileMetrics.NUMBER_OF_FUNCTIONS.metricName] as Double).isEqualTo(1.0) + } +} diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/empty.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/empty.cc.json index 2711c6018d..3ee0351e94 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/empty.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/empty.cc.json @@ -1,5 +1,4 @@ { - "checksum": "f983aebcae5f795440bf7a8ec6cadba1", "data": { "projectName": "", "nodes": [ @@ -152,9 +151,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -268,5 +269,6 @@ } }, "blacklist": [] - } + }, + "checksum": "cbe157f368d090f1a5105f10338e9ff7" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/excludePattern.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/excludePattern.cc.json index 610f6aac67..78f7ea9ac9 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/excludePattern.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/excludePattern.cc.json @@ -1,5 +1,4 @@ { - "checksum": "5b7a2f53493f8755370905f1a7c2d7a0", "data": { "projectName": "", "nodes": [ @@ -19,7 +18,7 @@ "number_of_functions": 1.0, "message_chains": 0.0, "rloc": 5.0, - "loc": 8.0, + "loc": 9.0, "long_method": 0.0, "long_parameter_list": 0.0, "excessive_comments": 0.0, @@ -217,9 +216,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -333,5 +334,6 @@ } }, "blacklist": [] - } + }, + "checksum": "8da2bef9265b511b8dedb9957779fd7d" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/includeAll.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/includeAll.cc.json index 73bc0c08b0..e0e869d8ca 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/includeAll.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/includeAll.cc.json @@ -1,5 +1,4 @@ { - "checksum": "52c78daa83acfe0bedc2cc526b4ffa12", "data": { "projectName": "", "nodes": [ @@ -163,7 +162,7 @@ "number_of_functions": 0.0, "message_chains": 0.0, "rloc": 9.0, - "loc": 14.0, + "loc": 15.0, "long_method": 0.0, "long_parameter_list": 0.0, "excessive_comments": 0.0, @@ -195,7 +194,7 @@ "number_of_functions": 1.0, "message_chains": 0.0, "rloc": 5.0, - "loc": 8.0, + "loc": 9.0, "long_method": 0.0, "long_parameter_list": 0.0, "excessive_comments": 0.0, @@ -393,9 +392,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -509,5 +510,6 @@ } }, "blacklist": [] - } + }, + "checksum": "0b7ea0e9f7fd367f2b7d50f323962432" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/kotlinOnly.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/kotlinOnly.cc.json index 6531b30327..7b6e93855b 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/kotlinOnly.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/kotlinOnly.cc.json @@ -1,5 +1,4 @@ { - "checksum": "4d74279a874cf465d39ea7b372f5255f", "data": { "projectName": "", "nodes": [ @@ -289,9 +288,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -405,5 +406,6 @@ } }, "blacklist": [] - } + }, + "checksum": "04f7882c34a1d9988a6f3f2d45eb51c8" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/bashSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/bashSample.cc.json index 7ef81920a7..dc5b5fd61e 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/bashSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/bashSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "d7516dbebfe70430239ed55eb36d6e22", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "5b265d6b4986fd9a235bbbed7f921528" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cHeaderSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cHeaderSample.cc.json index 72af7b72b1..d6cc7fa85d 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cHeaderSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cHeaderSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "b83a3a76a7dd2803565feee9ca154c50", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "0707f1b4d4c948a5bda3ac17a7985f80" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cSample.cc.json index a2d4b3cb44..35e80a857e 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "6f746426c61c9cc7feebb4a20258a772", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "e93e8849a0bb43f86156eed4ccc2085f" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cSharpSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cSharpSample.cc.json index cced6acccf..62d6930d73 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cSharpSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cSharpSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "bb15083cc4e241f0b498430d59ea3e1e", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "c6e21f260fdf0dd30fc62ba2f455d946" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cppHeaderSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cppHeaderSample.cc.json index a38ba24f07..0c3891f546 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cppHeaderSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cppHeaderSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "2bc2971a0aaaee3a68bd1d46492f9102", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "2b4d6494ae3823c98b606b53391cac21" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cppSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cppSample.cc.json index 48ce749df5..0edc9e621e 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cppSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/cppSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "ca2944954294c9489c94e290f7612a2c", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "f0202a5a013d084c41aca434a9abe88c" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/goSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/goSample.cc.json index 1c17a9123e..6095dc986b 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/goSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/goSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "23ac1df2b7ce9a8a97130baec6ec59d7", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "2a5c1930da1d40f7e81f2d271d1c49fa" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/javaSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/javaSample.cc.json index 6fcca05611..09357f8ef9 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/javaSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/javaSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "94082178d338632dd43716ce0014e45d", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "c08a655ca790705609de74a1a8af84a9" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/javascriptSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/javascriptSample.cc.json index acd1ae5e54..7c167ed821 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/javascriptSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/javascriptSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "b842641abfac9e1cc332cc15cb70e646", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "47b1d8cc700daa477d88604de96f025f" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/kotlinSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/kotlinSample.cc.json index a49d8f0d05..3b851395e7 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/kotlinSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/kotlinSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "85a4729e04aa2bf21897c374d80f7979", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "c5fc1295e9d7bd07cf666423848a3f2e" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/objectiveCSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/objectiveCSample.cc.json index 18b0196830..4418176a4a 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/objectiveCSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/objectiveCSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "6738c6df47917ac27c1aedb580ef9ce2", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "483671329bc7a2b3bad792dd3c2186e5" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/phpSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/phpSample.cc.json index bd7d268ced..90e6de042d 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/phpSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/phpSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "493784de4d7ca23986734e91d6fbc701", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "d0b4c45c88e412493daa9cefa42ff416" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/pythonSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/pythonSample.cc.json index b3ed1f5744..4552beda9a 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/pythonSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/pythonSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "931fcdc61768f9d2efef163757ab70ea", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "fae4d05ea92f15bd5b40344d07bd60b5" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/rubySample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/rubySample.cc.json index 66bb42480a..9efafd558a 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/rubySample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/rubySample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "0289156540786ae61f5a308eb70496b1", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "32989886dfa9a997eec246f3b33e7c69" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/swiftSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/swiftSample.cc.json index 6eaf966ec9..7423354208 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/swiftSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/swiftSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "e43cdfa3d10827f01d089f79d3e4e179", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "f8e09025e00d3b323e7fd856e7dbaa63" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/tsxSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/tsxSample.cc.json new file mode 100644 index 0000000000..d22dad9e28 --- /dev/null +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/tsxSample.cc.json @@ -0,0 +1,307 @@ +{ + "data": { + "projectName": "", + "nodes": [ + { + "name": "root", + "type": "Folder", + "attributes": {}, + "link": "", + "children": [ + { + "name": "tsxSample.tsx", + "type": "File", + "attributes": { + "complexity": 23.0, + "logic_complexity": 16.0, + "comment_lines": 25.0, + "number_of_functions": 3.0, + "message_chains": 0.0, + "rloc": 69.0, + "loc": 109.0, + "long_method": 2.0, + "long_parameter_list": 0.0, + "excessive_comments": 1.0, + "comment_ratio": 0.36, + "max_parameters_per_function": 2.0, + "min_parameters_per_function": 1.0, + "mean_parameters_per_function": 1.33, + "median_parameters_per_function": 1.0, + "max_complexity_per_function": 9.0, + "min_complexity_per_function": 3.0, + "mean_complexity_per_function": 6.67, + "median_complexity_per_function": 8.0, + "max_rloc_per_function": 18.0, + "min_rloc_per_function": 8.0, + "mean_rloc_per_function": 14.67, + "median_rloc_per_function": 18.0 + }, + "link": "", + "children": [], + "checksum": "63beee9616698ee1" + } + ] + } + ], + "apiVersion": "1.5", + "edges": [], + "attributeTypes": {}, + "attributeDescriptors": { + "complexity": { + "title": "Complexity", + "description": "Complexity of the file representing how much cognitive load is needed to overview the whole file", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "logic_complexity": { + "title": "Logic complexity", + "description": "Complexity of the file based on number of paths through the code, similar to cyclomatic complexity", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://en.wikipedia.org/wiki/Cyclomatic_complexity", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "comment_lines": { + "title": "Comment lines", + "description": "Number of lines containing either a comment or commented-out code", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "loc": { + "title": "Lines of Code", + "description": "Lines of code including empty lines and comments", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "rloc": { + "title": "Real Lines of Code", + "description": "Number of lines that contain at least one character which is neither a whitespace nor a tabulation nor part of a comment", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "number_of_functions": { + "title": "Number of functions", + "description": "The number of functions or methods present in the file. Does not include anonymous or lambda functions.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "max_parameters_per_function": { + "title": "Maximum parameters per function", + "description": "The maximum number of parameters a function or method has for this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "min_parameters_per_function": { + "title": "Minimum parameters per function", + "description": "The minimum number of parameters a function or method has for this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "median_parameters_per_function": { + "title": "Median parameters per function", + "description": "The median number of parameters a function or method has for this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "mean_parameters_per_function": { + "title": "Mean parameters per function", + "description": "The mean number of parameters a function or method has for this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "max_complexity_per_function": { + "title": "Maximum complexity per function", + "description": "The maximum complexity in the body of a function of this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "min_complexity_per_function": { + "title": "Minimum complexity per function", + "description": "The minimum number of complexity in the body of a function of this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "mean_complexity_per_function": { + "title": "Mean complexity per function", + "description": "The mean complexity found in the body of a function of this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "median_complexity_per_function": { + "title": "Median complexity per function", + "description": "The median complexity found in the body of a function of this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "max_rloc_per_function": { + "title": "Maximum real lines of code in a function", + "description": "The maximum number of real lines of code in a function of this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "min_rloc_per_function": { + "title": "Minimum real lines of code in a function", + "description": "The minimum number of real lines of code in a function of this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "mean_rloc_per_function": { + "title": "Mean real lines of code in a function", + "description": "The mean number of real lines of code in a function of this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "median_rloc_per_function": { + "title": "Median real lines of code in a function", + "description": "The median number of real lines of code in a function of this file.", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "long_method": { + "title": "Long Method", + "description": "Code smell showing the number of functions with more than 10 real lines of code (rloc)", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "long_parameter_list": { + "title": "Long Parameter List", + "description": "Code smell showing the number of functions with more than 4 parameters", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "excessive_comments": { + "title": "Excessive Comments", + "description": "Code smell showing whether a file has more than 10 comment lines", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + }, + "comment_ratio": { + "title": "Comment Ratio", + "description": "The ratio of comment lines to real lines of code (rloc)", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": 0, + "analyzers": [ + "unifiedParser" + ] + }, + "message_chains": { + "title": "Message Chains", + "description": "Code smell showing occurrences of method call chains with 4 or more consecutive calls suggesting tight coupling", + "hintLowValue": "", + "hintHighValue": "", + "link": "https://codecharta.com/docs/parser/unified", + "direction": -1, + "analyzers": [ + "unifiedParser" + ] + } + }, + "blacklist": [] + }, + "checksum": "e89455844b9df25247764607e28a5f30" +} diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/tsxSample.tsx b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/tsxSample.tsx new file mode 100644 index 0000000000..6cf5ccb36d --- /dev/null +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/tsxSample.tsx @@ -0,0 +1,109 @@ +/** + * This is a generated sample file for analysis only + */ + +import React, { useState } from "react"; + +/** + * Props for the UserCard component + */ +interface UserCardProps { + name: string; + email: string; + role: "admin" | "user" | "guest"; + isActive: boolean; +} + +/** + * Displays a card with user information + */ +function UserCard({ name, email, role, isActive }: UserCardProps): JSX.Element { + // Apply different styles based on role + const roleColor = role === "admin" ? "red" : role === "user" ? "blue" : "gray"; + + return ( +
+

{name}

+

{email}

+ {role} +
+ ); +} + +/** + * Props for the UserList component + */ +interface UserListProps { + users: UserCardProps[]; + filterRole?: "admin" | "user" | "guest"; +} + +/** + * Renders a filterable list of user cards + */ +function UserList({ users, filterRole }: UserListProps): JSX.Element { + const [searchText, setSearchText] = useState(""); + + // Filter by role if specified + const filteredByRole = filterRole ? users.filter((u) => u.role === filterRole) : users; + + // Filter by search text + const visibleUsers = filteredByRole.filter((u) => { + if (searchText.length === 0) { + return true; + } + return u.name.toLowerCase().includes(searchText.toLowerCase()) || u.email.toLowerCase().includes(searchText.toLowerCase()); + }); + + return ( +
+ setSearchText(e.target.value)} placeholder="Search users..." /> + {visibleUsers.length === 0 ? ( +

No users found.

+ ) : ( + visibleUsers.map((user) => ) + )} +
+ ); +} + +/** + * Utility to determine access level — intentionally complex for metric coverage + */ +function determineAccessLevel(user: UserCardProps, resource: string): number { + // Inactive users have no access + if (!user.isActive) { + return 0; + } + + // Admins always have full access + if (user.role === "admin") { + return 3; + } + + if (resource.startsWith("personal-")) { + /* personal resources only for the user themselves */ + return user.role === "user" ? 2 : 0; + } else if (resource.startsWith("team-")) { + if (user.role === "user") { + return 2; + } else if (user.role === "guest") { + return 1; + } + } else if (resource === "public") { + return 1; + } + + return user.role === "guest" ? 0 : 1; +} + +// Example usage +const sampleUsers: UserCardProps[] = [ + { name: "Alice", email: "alice@example.com", role: "admin", isActive: true }, + { name: "Bob", email: "bob@example.com", role: "user", isActive: true }, + { name: "Carol", email: "carol@example.com", role: "guest", isActive: false }, +]; + +console.log(determineAccessLevel(sampleUsers[0], "team-finance")); +console.log(determineAccessLevel(sampleUsers[1], "personal-1")); +console.log(determineAccessLevel(sampleUsers[2], "public")); diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/typescriptSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/typescriptSample.cc.json index 2022b0866a..6847621faf 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/typescriptSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/typescriptSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "c3f1f5f78223008a943aea64fc84116e", "data": { "projectName": "", "nodes": [ @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "87bea20f6ca0541428887295af376962" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/vueSample.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/vueSample.cc.json index cf9b4c043f..3a98658c36 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/vueSample.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/languageSamples/vueSample.cc.json @@ -1,5 +1,4 @@ { - "checksum": "47001530a4597f15242216cfe93739d5", "data": { "projectName": "", "nodes": [ @@ -19,7 +18,7 @@ "number_of_functions": 8.0, "message_chains": 0.0, "rloc": 69.0, - "loc": 84.0, + "loc": 85.0, "long_method": 1.0, "long_parameter_list": 0.0, "excessive_comments": 1.0, @@ -185,9 +184,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -301,5 +302,6 @@ } }, "blacklist": [] - } + }, + "checksum": "849d945434998b95f51c391bd41dda15" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/mergeResult.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/mergeResult.cc.json index 8e18035b41..4e7a71c723 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/mergeResult.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/mergeResult.cc.json @@ -1,5 +1,4 @@ { - "checksum": "6e43c919775546b88be9046f94356a01", "data": { "projectName": "", "nodes": [ @@ -141,7 +140,7 @@ "number_of_functions": 0.0, "message_chains": 0.0, "rloc": 9.0, - "loc": 14.0, + "loc": 15.0, "long_method": 0.0, "long_parameter_list": 0.0, "excessive_comments": 0.0, @@ -173,7 +172,7 @@ "number_of_functions": 1.0, "message_chains": 0.0, "rloc": 5.0, - "loc": 8.0, + "loc": 9.0, "long_method": 0.0, "long_parameter_list": 0.0, "excessive_comments": 0.0, @@ -383,10 +382,10 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, "analyzers": [ - "Unknown" + "unifiedParser" ] }, "median_complexity_per_function": { @@ -501,5 +500,6 @@ } }, "blacklist": [] - } + }, + "checksum": "880d4dcef1c51873939f9105dc4afb11" } diff --git a/analysis/analysers/parsers/UnifiedParser/src/test/resources/sampleProject.cc.json b/analysis/analysers/parsers/UnifiedParser/src/test/resources/sampleProject.cc.json index 0bec0f8caf..61043fa975 100644 --- a/analysis/analysers/parsers/UnifiedParser/src/test/resources/sampleProject.cc.json +++ b/analysis/analysers/parsers/UnifiedParser/src/test/resources/sampleProject.cc.json @@ -1,5 +1,4 @@ { - "checksum": "aa9b37b414625bf132a81d323e3e5b75", "data": { "projectName": "", "nodes": [ @@ -123,7 +122,7 @@ "number_of_functions": 0.0, "message_chains": 0.0, "rloc": 9.0, - "loc": 14.0, + "loc": 15.0, "long_method": 0.0, "long_parameter_list": 0.0, "excessive_comments": 0.0, @@ -155,7 +154,7 @@ "number_of_functions": 1.0, "message_chains": 0.0, "rloc": 5.0, - "loc": 8.0, + "loc": 9.0, "long_method": 0.0, "long_parameter_list": 0.0, "excessive_comments": 0.0, @@ -353,9 +352,11 @@ "description": "The mean complexity found in the body of a function of this file.", "hintLowValue": "", "hintHighValue": "", - "link": "", + "link": "https://codecharta.com/docs/parser/unified", "direction": -1, - "analyzers": [] + "analyzers": [ + "unifiedParser" + ] }, "median_complexity_per_function": { "title": "Median complexity per function", @@ -469,5 +470,6 @@ } }, "blacklist": [] - } + }, + "checksum": "4dad66260a46298f0ab7ea55b6f12aee" } diff --git a/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/FileExtension.kt b/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/FileExtension.kt index 914b90099f..da394b4ba9 100644 --- a/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/FileExtension.kt +++ b/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/FileExtension.kt @@ -1,6 +1,12 @@ package de.maibornwolff.codecharta.serialization -enum class FileExtension(val primaryExtension: String, val otherValidExtensions: Set = setOf()) { +/** Canonical file extensions recognized by CodeCharta analysers. */ +enum class FileExtension( + /** The primary extension string, including the leading dot (e.g. `".java"`). */ + val primaryExtension: String, + /** Additional valid extensions for this language beyond the primary one. */ + val otherValidExtensions: Set = setOf() +) { JSON(".json"), CSV(".csv"), CODECHARTA(".cc"), @@ -12,6 +18,7 @@ enum class FileExtension(val primaryExtension: String, val otherValidExtensions: GO(".go"), PHP(".php"), TYPESCRIPT(".ts", setOf("cts", ".mts")), + TSX(".tsx"), CSHARP(".cs"), CPP(".cpp", setOf(".cc", ".cxx", ".c++", ".hh", ".hpp", ".hxx")), C(".c", setOf(".h")), diff --git a/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectInputReader.kt b/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectInputReader.kt index dcfbdfbd33..7c8ee86d85 100644 --- a/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectInputReader.kt +++ b/analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/ProjectInputReader.kt @@ -7,18 +7,26 @@ import java.io.InputStream import java.io.InputStreamReader import java.util.Scanner +/** Reads and extracts serialized project JSON from an [java.io.InputStream], handling pipe-based input with sync signalling. */ object ProjectInputReader { -/** + private const val MAX_WAIT_MS = 500L + private const val CHECK_INTERVAL_MS = 50L + + /** * Extracts a JSON string representing a project from the given InputStream. * Because piped bash commands run concurrently, a pipeable ccsh-parser sends a sync flag * to signal other parsers to check for piped input. - * A short wait ensures the availability of potential sync flags. + * Polls for data availability to handle concurrent JVM startup delays. * * @param input InputStream with serialized project data. * @return JSON string of the project, or an empty string if no valid data is found. */ fun extractProjectString(input: InputStream): String { - Thread.sleep(100) + var waited = 0L + while (input.available() <= 0 && waited < MAX_WAIT_MS) { + Thread.sleep(CHECK_INTERVAL_MS) + waited += CHECK_INTERVAL_MS + } val availableBytes = input.available() if (availableBytes <= 0) { return "" @@ -50,6 +58,7 @@ object ProjectInputReader { return content.replace(Regex("[\\n\\r]"), "") } + /** Returns `true` if the first 1024 bytes of [input] contain the ccsh sync flag, leaving the stream reset to its original position. */ private fun containsSyncSignal(input: InputStream): Boolean { val bufferSize = 1024 val buffer = ByteArray(bufferSize) @@ -62,6 +71,7 @@ object ProjectInputReader { return isSubarray(syncSignalBytes, buffer) } + /** Extracts the last complete JSON object from [streamContent] by scanning backwards for a balanced `{}`pair, returning the full string if no valid object is found. */ private fun extractJsonObjectFromEndOfStream(streamContent: String): String { var count = 0 val openingBracket = '{' @@ -90,6 +100,7 @@ object ProjectInputReader { } } + /** Returns `true` if [subarray] appears as a contiguous subsequence anywhere within [buffer]. */ private fun isSubarray(subarray: ByteArray, buffer: ByteArray): Boolean { for (i in 0 until buffer.size - subarray.size + 1) { if (buffer.copyOfRange(i, i + subarray.size).contentEquals(subarray)) { diff --git a/gh-pages/_docs/05-parser/05-unified.md b/gh-pages/_docs/05-parser/05-unified.md index dd3d03acb6..61e0f21b87 100644 --- a/gh-pages/_docs/05-parser/05-unified.md +++ b/gh-pages/_docs/05-parser/05-unified.md @@ -20,6 +20,7 @@ CodeCharta. It generates either a cc.json or a csv file. |--------------|----------------------------------------| | Javascript | .js, .cjs, .mjs | | Typescript | .ts, .cts, .mts | +| TSX | .tsx | | Java | .java | | Kotlin | .kt | | C# | .cs | @@ -32,6 +33,7 @@ CodeCharta. It generates either a cc.json or a csv file. | Ruby | .rb | | Swift | .swift | | Bash | .sh | +| Vue | .vue | ## Supported Metrics @@ -146,6 +148,14 @@ contribute to complexity: `class_static_block`, `function_expression` - **Logical operators**: `&&`, `||`, `??` in binary expressions +#### TSX (.tsx) + +- **Control flow**: `if_statement`, `do_statement`, `for_statement`, `while_statement`, `for_in_statement`, `ternary_expression`, + `conditional_type`, `switch_case`, `switch_default`, `catch_clause` +- **Functions**: `function_declaration`, `generator_function_declaration`, `arrow_function`, `generator_function`, `method_definition`, + `class_static_block`, `function_expression` +- **Logical operators**: `&&`, `||`, `??` in binary expressions + #### Java (.java) - **Control flow**: `if_statement`, `do_statement`, `for_statement`, `while_statement`, `enhanced_for_statement`, `ternary_expression`, @@ -235,7 +245,7 @@ contribute to complexity: Comment lines are counted based on language-specific comment syntax: -- **JavaScript/TypeScript**: `comment`, `html_comment` +- **JavaScript/TypeScript/TSX**: `comment`, `html_comment` - **Java**: `block_comment`, `line_comment` - **Kotlin**: `line_comment`, `multiline_comment` - **C#**: `comment` @@ -262,6 +272,11 @@ Function counting identifies different types of function definitions per languag - **Simple functions**: `function_declaration`, `generator_function_declaration`, `method_definition`, `function_expression` - **Arrow functions**: Assigned to variables (detected via `variable_declarator` with `arrow_function` value) +#### TSX (.tsx) + +- **Simple functions**: `function_declaration`, `generator_function_declaration`, `method_definition`, `function_expression` +- **Arrow functions**: Assigned to variables (detected via `variable_declarator` with `arrow_function` value) + #### Java (.java) - **Methods and constructors**: `method_declaration`, `constructor_declaration`, `compact_constructor_declaration` @@ -338,7 +353,7 @@ This metric is calculated by counting all lines that are not identified as comme Parameters per function counts the number of parameters declared for each function. The metric identifies parameter nodes specific to each language: -- **JavaScript/TypeScript**: `formal_parameter`, `required_parameter` +- **JavaScript/TypeScript/TSX**: `formal_parameter`, `required_parameter` - **Java**: `formal_parameter` - **Kotlin**: `parameter` - **C#**: `parameter` @@ -367,6 +382,11 @@ can indicate tight coupling and violations of the Law of Demeter. The metric cou - **Chain nodes**: `call_expression`, `member_expression` - **Call nodes**: `call_expression` +#### TSX (.tsx) + +- **Chain nodes**: `call_expression`, `member_expression` +- **Call nodes**: `call_expression` + #### Java (.java) - **Chain nodes**: `method_invocation`, `field_access` diff --git a/plans/add-tsx-support.md b/plans/add-tsx-support.md new file mode 100644 index 0000000000..c99aa8d648 --- /dev/null +++ b/plans/add-tsx-support.md @@ -0,0 +1,65 @@ +--- +name: add-tsx-support +issue: +state: complete +version: +--- + +## Goal + +Wire up the `Language.TSX` support added in treesitter-excavation v0.4.0 so the UnifiedParser discovers and parses `.tsx` files. + +## Tasks + +### 1. Bump treesitter-excavation dependency + +Update the version in `build.gradle.kts` to the release that includes `Language.TSX` (v0.4.1). + +- File: `analysis/analysers/parsers/UnifiedParser/build.gradle.kts` +- Change: `implementation("com.github.MaibornWolff:TreeSitterExcavationSite:v0.4.1")` → latest version with TSX + +After bumping, run all unit tests to catch any regressions from the version change: +```bash +cd analysis && ./gradlew :analysers:parsers:UnifiedParser:test +``` + +### 2. Write a failing test + +Create a new `TsxCollectorTest.kt` that creates a `TreeSitterLibraryCollector(Language.TSX)` and asserts LOC > 0 for a minimal `.tsx` snippet (e.g. a React functional component returning JSX). + +- New file: `analysis/analysers/parsers/UnifiedParser/src/test/kotlin/.../metriccollectors/TsxCollectorTest.kt` + +### 3. Add `TSX` to `FileExtension` enum + +Add a new entry so the project scanner discovers `.tsx` files. + +- File: `analysis/model/src/main/kotlin/de/maibornwolff/codecharta/serialization/FileExtension.kt` +- Add `TSX(".tsx")` after the `TYPESCRIPT` entry + +### 4. Register `TSX` collector in `AvailableCollectors` + +Map `FileExtension.TSX` to `Language.TSX`. + +- File: `analysis/analysers/parsers/UnifiedParser/src/main/kotlin/.../metriccollectors/AvailableCollectors.kt` +- Add `TSX(FileExtension.TSX, { TreeSitterLibraryCollector(Language.TSX) })` + +### 5. Add TSX mapping in `TreeSitterAdapter` + +Add `FileExtension.TSX -> Language.TSX` to `getLanguageForExtension()`. + +- File: `analysis/analysers/parsers/UnifiedParser/src/main/kotlin/.../metriccollectors/TreeSitterAdapter.kt` + +### 6. Confirm green + +Run tests and verify the new test passes along with all existing tests. + +## Steps + +- [x] Complete Task 1: Bump dependency (`v0.2.0` → `v0.4.1`) — already staged in `build.gradle.kts` +- [x] Complete Task 1 (cont.): Fix golden file regressions from v0.4.1 +- [x] Complete Task 2: Write failing TSX test +- [x] Complete Task 3: Add `TSX` to `FileExtension` +- [x] Complete Task 4: Register in `AvailableCollectors` +- [x] Complete Task 5: Add mapping in `TreeSitterAdapter` +- [x] Complete Task 6: All tests green +