UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

227 lines (226 loc) 7.87 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.customDimensions = void 0; exports.startAnalytics = startAnalytics; exports.reportNxAddCommand = reportNxAddCommand; exports.reportNxGenerateCommand = reportNxGenerateCommand; exports.reportCommandRunEvent = reportCommandRunEvent; exports.reportEvent = reportEvent; exports.argsToQueryString = argsToQueryString; exports.flushAnalytics = flushAnalytics; const nx_json_1 = require("../config/nx-json"); const workspace_root_1 = require("../utils/workspace-root"); const versions_1 = require("../utils/versions"); const native_1 = require("../native"); const package_manager_1 = require("../utils/package-manager"); const semver_1 = require("semver"); const os = require("os"); const machine_id_cache_1 = require("../utils/machine-id-cache"); const is_ci_1 = require("../utils/is-ci"); const analytics_prompt_1 = require("../utils/analytics-prompt"); const db_connection_1 = require("../utils/db-connection"); // Conditionally import telemetry functions only on non-WASM platforms let initializeTelemetry; let initializeTelemetryWithSessionId; let flushTelemetry; let trackEventNative; let trackPageViewNative; let getEventDimensions; if (!native_1.IS_WASM) { const nativeModule = require('../native'); initializeTelemetry = nativeModule.initializeTelemetry; initializeTelemetryWithSessionId = nativeModule.initializeTelemetryWithSessionId; flushTelemetry = nativeModule.flushTelemetry; trackEventNative = nativeModule.trackEvent; trackPageViewNative = nativeModule.trackPageView; getEventDimensions = nativeModule.getEventDimensions; } exports.customDimensions = native_1.IS_WASM ? null : (getEventDimensions?.() ?? null); let _telemetryInitialized = false; async function startAnalytics() { // Analytics not supported on WASM if (native_1.IS_WASM) { return; } if (!isAnalyticsEnabled()) { return; } const nxJson = (0, nx_json_1.readNxJson)(workspace_root_1.workspaceRoot); const workspaceId = (0, analytics_prompt_1.generateWorkspaceId)(); if (!workspaceId) { // Not a git repo — no telemetry return; } const isNxCloud = !!(nxJson?.nxCloudId ?? nxJson?.nxCloudAccessToken); const userId = await (0, machine_id_cache_1.getCurrentMachineId)(); const packageManagerInfo = getPackageManagerInfo(); const nodeVersion = (0, semver_1.parse)(process.version); const nodeVersionString = nodeVersion ? `${nodeVersion.major}.${nodeVersion.minor}.${nodeVersion.patch}` : 'unknown'; const commonArgs = [ workspaceId, userId, versions_1.nxVersion, packageManagerInfo.name, packageManagerInfo.version, nodeVersionString, os.arch(), os.platform(), os.release(), !!(0, is_ci_1.isCI)(), isNxCloud, ]; try { const sessionId = process.env.NX_ANALYTICS_SESSION_ID; if (sessionId) { // Plugin worker path — reuse session ID from parent, no DB needed initializeTelemetryWithSessionId(sessionId, ...commonArgs); } else { // CLI/daemon path — get session from DB, set env var for children const dbConnection = (0, db_connection_1.getDbConnection)(); const newSessionId = initializeTelemetry(dbConnection, ...commonArgs); process.env.NX_ANALYTICS_SESSION_ID = newSessionId; } _telemetryInitialized = true; // Flush analytics automatically on process exit so every code path // is covered without needing explicit exitAndFlushAnalytics() calls. process.on('exit', () => { flushAnalytics(); }); } catch (error) { // If telemetry service fails to initialize, continue without it if (process.env.NX_VERBOSE_LOGGING === 'true') { console.log(`Failed to initialize telemetry: ${error.message}`); } } } function reportNxAddCommand(packageName, version) { reportCommandRunEvent('add', { [exports.customDimensions.packageName]: packageName, [exports.customDimensions.packageVersion]: version, }); } function reportNxGenerateCommand(generator) { reportCommandRunEvent('generate', { [exports.customDimensions.generatorName]: generator, }); } function reportCommandRunEvent(command, parameters, args) { command = command === 'g' ? 'generate' : command; let pageLocation = command; if (args) { const qs = argsToQueryString(args); if (qs) { pageLocation = `${command}?${qs}`; } } trackPageView(command, pageLocation, parameters); } function reportEvent(name, eventParameters) { trackEvent(name, eventParameters); } const SKIP_ARGS_KEYS = new Set([ '$0', '_', '__overrides_unparsed__', '__overrides__', ]); // String args with fixed enum values that are safe to include in analytics. // All boolean and number args are included automatically. const ALLOWED_STRING_ARGS = new Set([ 'outputStyle', 'type', 'view', 'access', 'preset', 'interactive', 'printConfig', 'resolveVersionPlans', ]); function argsToQueryString(args) { const params = new URLSearchParams(); for (const [key, value] of Object.entries(args)) { if (SKIP_ARGS_KEYS.has(key)) continue; if (value === undefined || value === null) continue; if (typeof value === 'boolean' || typeof value === 'number') { params.append(key, String(value)); } else if (typeof value === 'string' && ALLOWED_STRING_ARGS.has(key)) { params.append(key, value); } // All other types (strings, arrays, objects) are dropped } return params.toString(); } function trackEvent(eventName, parameters) { if (_telemetryInitialized) { // Convert parameters to string map for Rust const stringParams = {}; if (parameters) { for (const [key, value] of Object.entries(parameters)) { if (value !== undefined && value !== null) { stringParams[key] = String(value); } } } // Fire and forget - synchronous call try { trackEventNative(eventName, stringParams); } catch { // Silently ignore errors } } } function trackPageView(pageTitle, pageLocation, parameters) { if (_telemetryInitialized) { // Convert parameters to string map for Rust const stringParams = {}; if (parameters) { for (const [key, value] of Object.entries(parameters)) { if (value !== undefined && value !== null) { stringParams[key] = String(value); } } } // Fire and forget - synchronous call try { trackPageViewNative(pageTitle, pageLocation, stringParams); } catch { // Silently ignore errors } } } function flushAnalytics() { if (_telemetryInitialized) { try { flushTelemetry(); } catch (error) { // Failure to report analytics shouldn't crash the CLI if (process.env.NX_VERBOSE_LOGGING === 'true') { console.log(`Failed to flush telemetry: ${error.message}`); } } } } function getPackageManagerInfo() { const pm = (0, package_manager_1.detectPackageManager)(); return { name: pm, version: (0, package_manager_1.getPackageManagerVersion)(pm), }; } function isAnalyticsEnabled() { const nxJson = (0, nx_json_1.readNxJson)(workspace_root_1.workspaceRoot); return nxJson?.analytics === true; }