Skip to content

JS-1453 Add TS ecosystem telemetry for project analysis#6615

Open
zglicz wants to merge 21 commits intomasterfrom
codex/js-1453-language-version-telemetry
Open

JS-1453 Add TS ecosystem telemetry for project analysis#6615
zglicz wants to merge 21 commits intomasterfrom
codex/js-1453-language-version-telemetry

Conversation

@zglicz
Copy link
Contributor

@zglicz zglicz commented Mar 17, 2026

Summary

  • add project-analysis telemetry collection on JS/TS side for TypeScript version, native preview detection, compiler options union, ECMAScript versions union, and TS program creation counters
  • send project telemetry in the meta websocket payload and extend bridge DTOs to carry it
  • report telemetry in plugin under javascript.telemetry.* while keeping existing javascript.runtime.* keys unchanged
  • add unit coverage for telemetry key emission and keep generated README count update from tooling

Validation

  • npm run bridge:compile
  • mvn -pl sonar-plugin/sonar-javascript-plugin -am -Dtest=PluginTelemetryTest,WebSensorTest -Dsurefire.failIfNoSpecifiedTests=false test

@zglicz zglicz requested a review from a team March 17, 2026 10:37
@hashicorp-vault-sonar-prod
Copy link

hashicorp-vault-sonar-prod bot commented Mar 17, 2026

JS-1453

@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2026

Ruling Report

No changes to ruling expected issues in this PR

Copy link
Contributor Author

@zglicz zglicz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also like to have some js tests and verify that the telemetry object gets populated properly. We have multiple good folders with fixtures setup and different tsconfigs, package.jsons. We want to have good coverage on this data aquisition.

Copy link
Contributor Author

@zglicz zglicz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this serialization of the compiler options is very verbose and should be using some built in/npm package. Also, fix the sonarqube issues

Comment on lines +254 to +257
const tsProgram = createStandardProgram(programOptions, {
onProgramCreated: () => telemetry.recordProgramCreationSuccess(),
onProgramCreationError: () => telemetry.recordProgramCreationFailure(),
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why pass the callbacks? They could be simply invoked in the createStandardProgram. I dont think we need to customize this functionality

@zglicz zglicz force-pushed the codex/js-1453-language-version-telemetry branch from 019c4c7 to bc5cd72 Compare March 17, 2026 13:48
@zglicz zglicz marked this pull request as ready for review March 17, 2026 14:29
@sonar-review-alpha
Copy link

sonar-review-alpha bot commented Mar 17, 2026

Summary

This PR adds project-level TypeScript ecosystem telemetry collection to the JS/TS analyzer. During project analysis, it tracks: TypeScript versions detected in package.json, native preview usage, compiler options union across all programs, ECMAScript versions used, and TS program creation counters (attempted/succeeded/failed). The telemetry is collected on the JS/TS side, transmitted through the bridge to the plugin, and reported under javascript.telemetry.* keys, while preserving existing javascript.runtime.* keys unchanged.

What reviewers should know

Start by reviewing the new telemetry.ts file to understand the collector design and normalization logic (especially how enum values and lib paths are converted to strings). Then trace how it flows: analyzeProject.ts resets and retrieves telemetry, analyzeWithProgram.ts passes the collector through analysis functions to record options/versions, createStandardProgram.ts wraps program creation to track success/failure. On the bridge side, note the new DTOs in BridgeServer.java. Finally, check PluginTelemetry.java to see how it formats the data for SonarQube. Key gotcha: telemetry uses global state that's reset per analysis—verify the initialization/cleanup pattern in tests. The normalization of compiler option values (especially for enum types and lib values) preserves readability while handling TypeScript's internal representations.


  • Generate Walkthrough
  • Generate Diagram

🗣️ Give feedback

Copy link

@sonar-review-alpha sonar-review-alpha bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conclusion: Good feature overall with clean layering, but there's a privacy bug that needs to be fixed before merge — certain compiler options would leak absolute filesystem paths into telemetry.

🗣️ Give feedback

@zglicz zglicz force-pushed the codex/js-1453-language-version-telemetry branch from fd30a63 to 27df753 Compare March 17, 2026 15:25
@zglicz zglicz requested a review from vdiez March 18, 2026 14:43
Copy link

@sonar-review-alpha sonar-review-alpha bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conclusion: One of the two previously flagged issues has been fixed; one remains open.

🗣️ Give feedback

Comment on lines +219 to +245
function getAvailablePackageJsons(): PackageJson[] {
const packageJsons: PackageJson[] = [];
for (const packageJsonFile of getPackageJsonFiles()) {
const packageJson = parsePackageJsonFile(packageJsonFile.content);
if (packageJson !== undefined) {
packageJsons.push(packageJson);
}
}
return packageJsons;
}

function getPackageJsonFiles(): Iterable<{ content: string | Buffer }> {
try {
return packageJsonStore.getPackageJsons().values();
} catch {
return [];
}
}

function parsePackageJsonFile(content: string | Buffer): PackageJson | undefined {
const packageJsonContent = typeof content === 'string' ? content : content.toString();
try {
return JSON.parse(stripBOM(packageJsonContent)) as PackageJson;
} catch {
return undefined;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic should go into the dependencies module handling packageJsons, if we want to keep the parsed contents I would rather keep them there

Comment on lines +330 to +333
function getTypeScriptOptionDeclarations(): TypeScriptOptionDeclaration[] {
return ((ts as unknown as { optionDeclarations?: unknown[] }).optionDeclarations ??
[]) as TypeScriptOptionDeclaration[];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better use CompilerOptions which is public

…6630)

Co-authored-by: Victor <victor.diez@sonarsource.com>
Copy link

@sonar-review-alpha sonar-review-alpha bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous logic duplication comment (ID 2947469058) has not been addressed. The refactor in this commit introduced getDependencyVersionSignal and isValidDependencySignal helpers, and partially applied them — but getNodeVersionSignal still uses an inline guard (typeof typesNode === 'string' && typesNode !== 'latest' && typesNode !== '*') instead of calling isValidDependencySignal(typesNode). The duplication remains.

🗣️ Give feedback

@sonarqube-next
Copy link

Copy link

@sonar-review-alpha sonar-review-alpha bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! ✅

🗣️ Give feedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants