1import { getUserStatePath } from '@expo/config/build/getUserState'; 2import JsonFile from '@expo/json-file'; 3import TelemetryClient from '@expo/rudder-sdk-node'; 4import crypto from 'crypto'; 5import { boolish } from 'getenv'; 6import os from 'os'; 7 8import { CommandOptions } from './types'; 9 10const packageJson = require('../package.json'); 11 12/** If telemetry is disabled by the user */ 13const EXPO_NO_TELEMETRY = boolish('EXPO_NO_TELEMETRY', false); 14/** If the tool is running in a sanboxed environment, either staging or local envs */ 15const EXPO_SANDBOX = boolish('EXPO_STAGING', false) || boolish('EXPO_LOCAL', false); 16 17/** The telemetry client instance to use */ 18let client: TelemetryClient | null = null; 19/** The anonymous identity ID */ 20let telemetryId: string | null = null; 21 22export function getTelemetryClient() { 23 if (!client) { 24 client = new TelemetryClient( 25 EXPO_SANDBOX ? '24TKICqYKilXM480mA7ktgVDdea' : '24TKR7CQAaGgIrLTgu3Fp4OdOkI', // expo unified, 26 'https://cdp.expo.dev/v1/batch', 27 { 28 flushInterval: 300, 29 } 30 ); 31 32 // Empty the telemetry queue on exit 33 process.on('SIGINT', () => client?.flush?.()); 34 process.on('SIGTERM', () => client?.flush?.()); 35 } 36 37 return client; 38} 39 40/** Get the randomly generated anonymous ID from the persistent storage, see @expo/cli */ 41async function getTelemetryIdAsync() { 42 const settings = new JsonFile<{ uuid?: string }>(getUserStatePath(), { 43 ensureDir: true, 44 jsonParseErrorDefault: {}, 45 cantReadFileDefault: {}, 46 }); 47 48 let id = await settings.getAsync('uuid', null); 49 50 if (!id) { 51 id = crypto.randomUUID(); 52 await settings.setAsync('uuid', id); 53 } 54 55 return id; 56} 57 58function getTelemetryContext() { 59 const PLATFORM_NAMES: Partial<Record<NodeJS.Platform, string>> = { 60 darwin: 'Mac', 61 win32: 'Windows', 62 linux: 'Linux', 63 }; 64 65 return { 66 os: { name: PLATFORM_NAMES[os.platform()] ?? os.platform(), version: os.release() }, 67 app: { name: 'create-expo-module', version: packageJson.version ?? undefined }, 68 }; 69} 70 71type Event = { 72 event: 'create expo module'; 73 properties: Record<string, any>; 74}; 75 76export async function logEventAsync(event: Event) { 77 if (EXPO_NO_TELEMETRY) { 78 return; 79 } 80 81 if (!telemetryId) { 82 telemetryId = await getTelemetryIdAsync(); 83 getTelemetryClient().identify({ anonymousId: telemetryId }); 84 } 85 86 const commonProperties = { 87 source: 'create-expo-module', 88 source_version: packageJson.version ?? undefined, 89 }; 90 91 getTelemetryClient().track({ 92 ...event, 93 properties: { ...event.properties, ...commonProperties }, 94 anonymousId: telemetryId, 95 context: getTelemetryContext(), 96 }); 97} 98 99export function eventCreateExpoModule(packageManager: string, options: CommandOptions) { 100 return { 101 event: 'create expo module' as const, // DO NOT EDIT, unless knowing what you are doing 102 properties: { 103 nodeVersion: process.version, 104 packageManager, 105 withTemplate: !!options.source, 106 withReadme: options.withReadme, 107 withChangelog: options.withChangelog, 108 withExample: options.example, 109 local: !!options.local, 110 }, 111 }; 112} 113