diff options
author | 2021-10-05 23:08:06 -0700 | |
---|---|---|
committer | 2021-10-05 23:08:06 -0700 | |
commit | 3b10cfae804e7a94421acf2f1573241091480153 (patch) | |
tree | a3878ede5cf15a7c1f6775bc7fecb0e853b9bd80 | |
parent | bd14ad9e6ed9343acc21483c0c44cf0b81d8bae6 (diff) | |
download | bun-3b10cfae804e7a94421acf2f1573241091480153.tar.gz bun-3b10cfae804e7a94421acf2f1573241091480153.tar.zst bun-3b10cfae804e7a94421acf2f1573241091480153.zip |
analytics is good enough for now
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | build-id | 2 | ||||
-rw-r--r-- | integration/scripts/browser.js | 4 | ||||
-rw-r--r-- | misctools/features.zig | 17 | ||||
-rw-r--r-- | src/analytics/analytics_schema.zig | 317 | ||||
-rw-r--r-- | src/analytics/analytics_thread.zig | 157 | ||||
-rw-r--r-- | src/analytics/schema.peechy | 4 | ||||
-rw-r--r-- | src/bundler.zig | 26 | ||||
-rw-r--r-- | src/env.zig | 2 | ||||
-rw-r--r-- | src/env_loader.zig | 11 | ||||
-rw-r--r-- | src/feature_flags.zig | 5 | ||||
-rw-r--r-- | src/http.zig | 21 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 2 | ||||
-rw-r--r-- | src/options.zig | 14 |
14 files changed, 440 insertions, 145 deletions
@@ -383,3 +383,6 @@ picohttp: analytics: ./node_modules/.bin/peechy --schema src/analytics/schema.peechy --zig src/analytics/analytics_schema.zig + +analytics-features: + @cd misctools; zig run --main-pkg-path ../ ./features.zig
\ No newline at end of file @@ -1 +1 @@ -32 +33 diff --git a/integration/scripts/browser.js b/integration/scripts/browser.js index f3ddefea9..2d758e638 100644 --- a/integration/scripts/browser.js +++ b/integration/scripts/browser.js @@ -16,6 +16,10 @@ const bunExec = process.env.BUN_BIN || "bun"; const bunProcess = child_process.spawn(bunExec, bunFlags, { cwd: snippetsDir, stdio: "pipe", + env: { + ...process.env, + DISABLE_BUN_ANALYTICS: "1", + }, shell: false, }); diff --git a/misctools/features.zig b/misctools/features.zig new file mode 100644 index 000000000..9886c12cb --- /dev/null +++ b/misctools/features.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +const path_handler = @import("../src/resolver/resolve_path.zig"); +usingnamespace @import("../src/global.zig"); +const Features = @import("../src/analytics/analytics_thread.zig").Features; + +// zig run --main-pkg-path ../ ./features.zig +pub fn main() anyerror!void { + var stdout_ = std.io.getStdOut(); + var stderr_ = std.io.getStdErr(); + var output_source = Output.Source.init(stdout_, stderr_); + Output.Source.set(&output_source); + defer Output.flush(); + + var writer = Output.writer(); + try Features.Serializer.writeAll(@TypeOf(writer), writer); +} diff --git a/src/analytics/analytics_schema.zig b/src/analytics/analytics_schema.zig index 917422ba5..08cecd8b3 100644 --- a/src/analytics/analytics_schema.zig +++ b/src/analytics/analytics_schema.zig @@ -1,3 +1,4 @@ + const std = @import("std"); pub const Reader = struct { @@ -281,193 +282,229 @@ pub fn Writer(comptime WritableStream: type) type { pub const ByteWriter = Writer(*std.io.FixedBufferStream([]u8)); pub const FileWriter = Writer(std.fs.File); -pub const analytics = struct { - pub const OperatingSystem = enum(u8) { - _none, - /// linux - linux, - /// macos - macos, - /// windows - windows, - /// wsl - wsl, +pub const analytics = struct { - _, +pub const OperatingSystem = enum(u8) { - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; +_none, + /// linux + linux, - pub const Architecture = enum(u8) { - _none, - /// x64 - x64, + /// macos + macos, - /// arm - arm, + /// windows + windows, - _, + /// wsl + wsl, - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; +_, - pub const Platform = struct { - /// os - os: OperatingSystem, + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } - /// arch - arch: Architecture, + +}; - /// version - version: []const u8, +pub const Architecture = enum(u8) { - pub fn decode(reader: anytype) anyerror!Platform { - var this = std.mem.zeroes(Platform); +_none, + /// x64 + x64, - this.os = try reader.readValue(OperatingSystem); - this.arch = try reader.readValue(Architecture); - this.version = try reader.readArray(u8); - return this; - } + /// arm + arm, - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeEnum(this.os); - try writer.writeEnum(this.arch); - try writer.writeArray(u8, this.version); - } - }; +_, - pub const EventKind = enum(u32) { - _none, - /// bundle_success - bundle_success, + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } - /// bundle_fail - bundle_fail, + +}; - /// http_start - http_start, +pub const Platform = struct { +/// os +os: OperatingSystem, - /// http_build - http_build, +/// arch +arch: Architecture, - /// bundle_start - bundle_start, +/// version +version: []const u8, - _, - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; +pub fn decode(reader: anytype) anyerror!Platform { + var this = std.mem.zeroes(Platform); - pub const Uint64 = packed struct { - /// first - first: u32 = 0, + this.os = try reader.readValue(OperatingSystem); + this.arch = try reader.readValue(Architecture); + this.version = try reader.readArray(u8); + return this; +} - /// second - second: u32 = 0, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeEnum(this.os); + try writer.writeEnum(this.arch); + try writer.writeArray(u8, this.version); +} - pub fn decode(reader: anytype) anyerror!Uint64 { - var this = std.mem.zeroes(Uint64); +}; - this.first = try reader.readValue(u32); - this.second = try reader.readValue(u32); - return this; - } +pub const EventKind = enum(u32) { - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.first); - try writer.writeInt(this.second); - } - }; +_none, + /// bundle_success + bundle_success, - pub const EventListHeader = struct { - /// machine_id - machine_id: Uint64, + /// bundle_fail + bundle_fail, - /// session_id - session_id: u32 = 0, + /// http_start + http_start, - /// platform - platform: Platform, + /// http_build + http_build, - /// build_id - build_id: u32 = 0, + /// bundle_start + bundle_start, - /// session_length - session_length: u32 = 0, +_, - pub fn decode(reader: anytype) anyerror!EventListHeader { - var this = std.mem.zeroes(EventListHeader); + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } - this.machine_id = try reader.readValue(Uint64); - this.session_id = try reader.readValue(u32); - this.platform = try reader.readValue(Platform); - this.build_id = try reader.readValue(u32); - this.session_length = try reader.readValue(u32); - return this; - } + +}; - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeValue(this.machine_id); - try writer.writeInt(this.session_id); - try writer.writeValue(this.platform); - try writer.writeInt(this.build_id); - try writer.writeInt(this.session_length); - } - }; +pub const Uint64 = packed struct { +/// first +first: u32 = 0, - pub const EventHeader = struct { - /// timestamp - timestamp: Uint64, +/// second +second: u32 = 0, - /// kind - kind: EventKind, - pub fn decode(reader: anytype) anyerror!EventHeader { - var this = std.mem.zeroes(EventHeader); +pub fn decode(reader: anytype) anyerror!Uint64 { + var this = std.mem.zeroes(Uint64); - this.timestamp = try reader.readValue(Uint64); - this.kind = try reader.readValue(EventKind); - return this; - } + this.first = try reader.readValue(u32); + this.second = try reader.readValue(u32); + return this; +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeValue(this.timestamp); - try writer.writeEnum(this.kind); - } - }; +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.first); + try writer.writeInt(this.second); +} - pub const EventList = struct { - /// header - header: EventListHeader, +}; - /// event_count - event_count: u32 = 0, +pub const EventListHeader = struct { +/// machine_id +machine_id: Uint64, - pub fn decode(reader: anytype) anyerror!EventList { - var this = std.mem.zeroes(EventList); +/// session_id +session_id: u32 = 0, - this.header = try reader.readValue(EventListHeader); - this.event_count = try reader.readValue(u32); - return this; - } +/// platform +platform: Platform, + +/// build_id +build_id: u32 = 0, + +/// project_id +project_id: Uint64, + +/// session_length +session_length: u32 = 0, + +/// feature_usage +feature_usage: u32 = 0, + + +pub fn decode(reader: anytype) anyerror!EventListHeader { + var this = std.mem.zeroes(EventListHeader); + + this.machine_id = try reader.readValue(Uint64); + this.session_id = try reader.readValue(u32); + this.platform = try reader.readValue(Platform); + this.build_id = try reader.readValue(u32); + this.project_id = try reader.readValue(Uint64); + this.session_length = try reader.readValue(u32); + this.feature_usage = try reader.readValue(u32); + return this; +} + +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(this.machine_id); + try writer.writeInt(this.session_id); + try writer.writeValue(this.platform); + try writer.writeInt(this.build_id); + try writer.writeValue(this.project_id); + try writer.writeInt(this.session_length); + try writer.writeInt(this.feature_usage); +} + +}; + +pub const EventHeader = struct { +/// timestamp +timestamp: Uint64, + +/// kind +kind: EventKind, + + +pub fn decode(reader: anytype) anyerror!EventHeader { + var this = std.mem.zeroes(EventHeader); + + this.timestamp = try reader.readValue(Uint64); + this.kind = try reader.readValue(EventKind); + return this; +} + +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(this.timestamp); + try writer.writeEnum(this.kind); +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeValue(this.header); - try writer.writeInt(this.event_count); - } - }; }; +pub const EventList = struct { +/// header +header: EventListHeader, + +/// event_count +event_count: u32 = 0, + + +pub fn decode(reader: anytype) anyerror!EventList { + var this = std.mem.zeroes(EventList); + + this.header = try reader.readValue(EventListHeader); + this.event_count = try reader.readValue(u32); + return this; +} + +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(this.header); + try writer.writeInt(this.event_count); +} + +}; + + +}; + + const ExamplePackedStruct = packed struct { len: u32 = 0, offset: u32 = 0, diff --git a/src/analytics/analytics_thread.zig b/src/analytics/analytics_thread.zig index 5484c3c0f..434bc0c7d 100644 --- a/src/analytics/analytics_thread.zig +++ b/src/analytics/analytics_thread.zig @@ -9,6 +9,124 @@ const Analytics = @import("./analytics_schema.zig").analytics; const Writer = @import("./analytics_schema.zig").Writer; const Headers = @import("../javascript/jsc/webcore/response.zig").Headers; +fn NewUint64(val: u64) Analytics.Uint64 { + const bytes = std.mem.asBytes(&val); + return .{ + .first = std.mem.readIntNative(u32, bytes[0..4]), + .second = std.mem.readIntNative(u32, bytes[4..]), + }; +} + +// This answers, "What parts of Bun are people actually using?" +pub const Features = struct { + pub var single_page_app_routing = false; + pub var tsconfig_paths = false; + pub var fast_refresh = false; + pub var hot_module_reloading = false; + pub var jsx = false; + pub var always_bundle = false; + pub var tsconfig = false; + pub var bun_bun = false; + pub var filesystem_router = false; + pub var framework = false; + pub var bunjs = false; + pub var macros = false; + pub var public_folder = false; + pub var dotenv = false; + pub var define = false; + pub var loaders = false; + pub var origin = false; + pub var external = false; + pub var fetch = false; + + const Bitset = std.bit_set.IntegerBitSet(32); + + pub const Serializer = struct { + inline fn shiftIndex(index: u32) !u32 { + return @intCast(u32, @as(Bitset.MaskInt, 1) << @intCast(Bitset.ShiftInt, index)); + } + + fn writeField(comptime WriterType: type, writer: WriterType, field_name: string, index: u32) !void { + var output: [64]u8 = undefined; + const name = std.ascii.upperString(&output, field_name); + + try writer.print("const Features_{s} = {d}\n", .{ name, shiftIndex(index) }); + } + + pub fn writeAll(comptime WriterType: type, writer: WriterType) !void { + try writer.writeAll("package analytics\n\n"); + try writeField(WriterType, writer, "single_page_app_routing", 1); + try writeField(WriterType, writer, "tsconfig_paths", 2); + try writeField(WriterType, writer, "fast_refresh", 3); + try writeField(WriterType, writer, "hot_module_reloading", 4); + try writeField(WriterType, writer, "jsx", 5); + try writeField(WriterType, writer, "always_bundle", 6); + try writeField(WriterType, writer, "tsconfig", 7); + try writeField(WriterType, writer, "bun_bun", 8); + try writeField(WriterType, writer, "filesystem_router", 9); + try writeField(WriterType, writer, "framework", 10); + try writeField(WriterType, writer, "bunjs", 11); + try writeField(WriterType, writer, "macros", 12); + try writeField(WriterType, writer, "public_folder", 13); + try writeField(WriterType, writer, "dotenv", 14); + try writeField(WriterType, writer, "define", 15); + try writeField(WriterType, writer, "loaders", 16); + try writeField(WriterType, writer, "origin", 17); + try writeField(WriterType, writer, "external", 18); + try writeField(WriterType, writer, "fetch", 19); + try writer.writeAll("\n"); + } + }; + + pub fn toInt() u32 { + var list = Bitset.initEmpty(); + list.setValue(1, Features.single_page_app_routing); + list.setValue(2, Features.tsconfig_paths); + list.setValue(3, Features.fast_refresh); + list.setValue(4, Features.hot_module_reloading); + list.setValue(5, Features.jsx); + list.setValue(6, Features.always_bundle); + list.setValue(7, Features.tsconfig); + list.setValue(8, Features.bun_bun); + list.setValue(9, Features.filesystem_router); + list.setValue(10, Features.framework); + list.setValue(11, Features.bunjs); + list.setValue(12, Features.macros); + list.setValue(13, Features.public_folder); + list.setValue(14, Features.dotenv); + list.setValue(15, Features.define); + list.setValue(16, Features.loaders); + list.setValue(17, Features.origin); + list.setValue(18, Features.external); + list.setValue(19, Features.fetch); + + if (comptime FeatureFlags.verbose_analytics) { + if (Features.single_page_app_routing) Output.pretty("<r><d>single_page_app_routing<r>,", .{}); + if (Features.tsconfig_paths) Output.pretty("<r><d>tsconfig_paths<r>,", .{}); + if (Features.fast_refresh) Output.pretty("<r><d>fast_refresh<r>,", .{}); + if (Features.hot_module_reloading) Output.pretty("<r><d>hot_module_reloading<r>,", .{}); + if (Features.jsx) Output.pretty("<r><d>jsx<r>,", .{}); + if (Features.always_bundle) Output.pretty("<r><d>always_bundle<r>,", .{}); + if (Features.tsconfig) Output.pretty("<r><d>tsconfig<r>,", .{}); + if (Features.bun_bun) Output.pretty("<r><d>bun_bun<r>,", .{}); + if (Features.filesystem_router) Output.pretty("<r><d>filesystem_router<r>,", .{}); + if (Features.framework) Output.pretty("<r><d>framework<r>,", .{}); + if (Features.bunjs) Output.pretty("<r><d>bunjs<r>,", .{}); + if (Features.macros) Output.pretty("<r><d>macros<r>,", .{}); + if (Features.public_folder) Output.pretty("<r><d>public_folder<r>,", .{}); + if (Features.dotenv) Output.pretty("<r><d>dotenv<r>,", .{}); + if (Features.define) Output.pretty("<r><d>define<r>,", .{}); + if (Features.loaders) Output.pretty("<r><d>loaders<r>,", .{}); + if (Features.origin) Output.pretty("<r><d>origin<r>,", .{}); + if (Features.external) Output.pretty("<r><d>external<r>,", .{}); + if (Features.fetch) Output.pretty("<r><d>fetch<r>,", .{}); + Output.prettyln("\n", .{}); + } + + return @as(u32, list.mask); + } +}; + pub const EventName = enum(u8) { bundle_success, bundle_fail, @@ -21,6 +139,7 @@ var random: std.rand.DefaultPrng = undefined; const DotEnv = @import("../env_loader.zig"); const platform_arch = if (Environment.isAarch64) Analytics.Architecture.arm else Analytics.Architecture.x64; +var project_id: Analytics.Uint64 = .{}; pub const Event = struct { timestamp: u64, @@ -58,12 +177,19 @@ var event_queue: EventQueue = undefined; pub const GenerateHeader = struct { pub fn generate() Analytics.EventListHeader { + if (comptime isDebug) { + if (project_id.first == 0 and project_id.second == 0) { + Output.prettyErrorln("warn: project_id is 0", .{}); + } + } + if (Environment.isMac) { return Analytics.EventListHeader{ .machine_id = GenerateMachineID.forMac() catch Analytics.Uint64{}, .platform = GeneratePlatform.forMac(), .build_id = comptime @truncate(u32, Global.build_id), .session_id = random.random.int(u32), + .project_id = project_id, }; } @@ -73,6 +199,7 @@ pub const GenerateHeader = struct { .platform = GeneratePlatform.forLinux(), .build_id = comptime @truncate(u32, Global.build_id), .session_id = random.random.int(u32), + .project_id = project_id, }; } @@ -292,6 +419,7 @@ pub const EventList = struct { const now = std.time.nanoTimestamp(); this.header.session_length = @truncate(u32, @intCast(u64, (now - start_time)) / std.time.ns_per_ms); + this.header.feature_usage = Features.toInt(); var list = Analytics.EventList{ .header = this.header, @@ -372,3 +500,32 @@ pub const EventList = struct { }; pub var is_ci = false; + +pub var username_only_for_determining_project_id_and_never_sent: string = ""; +pub fn setProjectID(folder_name_: string, package_name: string) void { + if (disabled) return; + + var hasher = std.hash.Wyhash.init(10); + + var folder_name = folder_name_; + + // The idea here is + // When you're working at a mid-large company + // Basically everyone has standardized laptops + // The hardware may differ, but the folder structure is typically identical + // But the username or home folder may differ + // So when we hash, we skip that if it exists + if (username_only_for_determining_project_id_and_never_sent.len > 0) { + if (std.mem.indexOf(u8, folder_name, username_only_for_determining_project_id_and_never_sent)) |i| { + const offset = i + username_only_for_determining_project_id_and_never_sent.len + 1; + if (folder_name.len > offset) { + folder_name = folder_name[offset..]; + } + } + } + hasher.update(folder_name); + hasher.update("@"); + if (package_name.len > 0) hasher.update(package_name); + if (package_name.len == 0) hasher.update("\"\""); + project_id = NewUint64(hasher.final()); +} diff --git a/src/analytics/schema.peechy b/src/analytics/schema.peechy index 0caad6615..6d924cf09 100644 --- a/src/analytics/schema.peechy +++ b/src/analytics/schema.peechy @@ -36,7 +36,11 @@ struct EventListHeader { uint32 session_id; Platform platform; uint32 build_id; + // hash of the folder name + Uint64 project_id; uint32 session_length; + // enum flags + uint32 feature_usage; } struct EventHeader { diff --git a/src/bundler.zig b/src/bundler.zig index f2487502f..f348230f9 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -772,6 +772,8 @@ pub const Bundler = struct { bundler.resolver.debug_logs = try DebugLogs.init(allocator); } + Analytics.Features.bun_bun = true; + always_bundled: { const root_package_json_resolved: _resolver.Result = bundler.resolver.resolve(bundler.fs.top_level_dir, "./package.json", .stmt) catch |err| { generator.log.addWarning(null, logger.Loc.Empty, "Please run `bun bun` from a directory containing a package.json.") catch unreachable; @@ -779,9 +781,14 @@ pub const Bundler = struct { }; const root_package_json = root_package_json_resolved.package_json orelse brk: { const read_dir = (bundler.resolver.readDirInfo(bundler.fs.top_level_dir) catch unreachable).?; + Analytics.Features.tsconfig = Analytics.Features.tsconfig or read_dir.tsconfig_json != null; break :brk read_dir.package_json.?; }; + Analytics.setProjectID(std.fs.path.dirname(root_package_json.source.path.text) orelse "/", root_package_json.name); + Analytics.Features.macros = Analytics.Features.macros or root_package_json.macros.count() > 0; + if (root_package_json.always_bundle.len > 0) { + Analytics.Features.always_bundle = true; var always_bundled_package_jsons = bundler.allocator.alloc(*PackageJSON, root_package_json.always_bundle.len) catch unreachable; var always_bundled_package_hashes = bundler.allocator.alloc(u32, root_package_json.always_bundle.len) catch unreachable; var i: u16 = 0; @@ -838,6 +845,8 @@ pub const Bundler = struct { this.bundler.options.jsx.supports_fast_refresh and bundler.options.platform.isWebLike(); + Analytics.Features.fast_refresh = this.bundler.options.jsx.supports_fast_refresh; + const resolve_queue_estimate = bundler.options.entry_points.len + @intCast(usize, @boolToInt(framework_config != null)) + @intCast(usize, @boolToInt(include_refresh_runtime)) + @@ -845,6 +854,7 @@ pub const Bundler = struct { if (bundler.router) |router| { defer this.bundler.resetStore(); + Analytics.Features.filesystem_router = true; const entry_points = try router.getEntryPoints(allocator); for (entry_points) |entry_point| { @@ -870,6 +880,8 @@ pub const Bundler = struct { try this.bundler.configureFramework(true); if (bundler.options.framework) |framework| { + Analytics.Features.framework = true; + if (framework.override_modules.keys.len > 0) { bundler.options.framework.?.override_modules_hashes = allocator.alloc(u64, framework.override_modules.keys.len) catch unreachable; for (framework.override_modules.keys) |key, i| { @@ -878,6 +890,7 @@ pub const Bundler = struct { } if (bundler.options.platform.isBun()) { if (framework.server.isEnabled()) { + Analytics.Features.bunjs = true; const resolved = try bundler.linker.resolver.resolve( bundler.fs.top_level_dir, framework.server.path, @@ -938,8 +951,17 @@ pub const Bundler = struct { this.bundler.resetStore(); - try this.pool.start(this); - try this.pool.wait(this); + if (bundler.options.platform != .bun) Analytics.enqueue(Analytics.EventName.bundle_start); + this.pool.start(this) catch |err| { + Analytics.enqueue(Analytics.EventName.bundle_fail); + return err; + }; + this.pool.wait(this) catch |err| { + Analytics.enqueue(Analytics.EventName.bundle_fail); + return err; + }; + if (bundler.options.platform != .bun) Analytics.enqueue(Analytics.EventName.bundle_success); + estimated_input_lines_of_code.* = generator.estimated_input_lines_of_code; // if (comptime !isRelease) { diff --git a/src/env.zig b/src/env.zig index 351b295b2..4dd26d56b 100644 --- a/src/env.zig +++ b/src/env.zig @@ -23,4 +23,4 @@ pub const isTest = std.builtin.is_test; pub const isLinux = std.Target.current.os.tag == .linux; pub const isAarch64 = std.Target.current.cpu.arch == .aarch64; -pub const analytics_url = "http://localhost:4000/events"; +pub const analytics_url = if (isDebug) "http://localhost:4000/events" else "http://i.bun.sh/events"; diff --git a/src/env_loader.zig b/src/env_loader.zig index 3be39003b..7b1acdc95 100644 --- a/src/env_loader.zig +++ b/src/env_loader.zig @@ -2,6 +2,7 @@ const std = @import("std"); const logger = @import("./logger.zig"); usingnamespace @import("./global.zig"); const CodepointIterator = @import("./string_immutable.zig").CodepointIterator; +const Analytics = @import("./analytics/analytics_thread.zig"); const Fs = @import("./fs.zig"); const Api = @import("./api/schema.zig").Api; const Variable = struct { @@ -461,6 +462,12 @@ pub const Loader = struct { Parser.parse(&source, this.allocator, this.map, true); } this.did_load_process = true; + + if (this.map.get("HOME")) |home_folder| { + Analytics.username_only_for_determining_project_id_and_never_sent = home_folder; + } else if (this.map.get("USER")) |home_folder| { + Analytics.username_only_for_determining_project_id_and_never_sent = home_folder; + } } // mostly for tests @@ -486,20 +493,24 @@ pub const Loader = struct { if (dir.hasComptimeQuery(".env.local")) { try this.loadEnvFile(fs, dir_handle, ".env.local", false); + Analytics.Features.dotenv = true; } if (comptime development) { if (dir.hasComptimeQuery(".env.development")) { try this.loadEnvFile(fs, dir_handle, ".env.development", false); + Analytics.Features.dotenv = true; } } else { if (dir.hasComptimeQuery(".env.production")) { try this.loadEnvFile(fs, dir_handle, ".env.production", false); + Analytics.Features.dotenv = true; } } if (dir.hasComptimeQuery(".env")) { try this.loadEnvFile(fs, dir_handle, ".env", false); + Analytics.Features.dotenv = true; } this.printLoaded(start); diff --git a/src/feature_flags.zig b/src/feature_flags.zig index a132d0642..777753561 100644 --- a/src/feature_flags.zig +++ b/src/feature_flags.zig @@ -70,7 +70,10 @@ pub const auto_import_buffer = false; pub const is_macro_enabled = true; +// pretend everything is always the macro environment +// useful for debugging the macro's JSX transform pub const force_macro = false; + pub const include_filename_in_jsx = false; -pub const verbose_analytics = true; +pub const verbose_analytics = false; diff --git a/src/http.zig b/src/http.zig index a9b0c14a3..0baa3b3b5 100644 --- a/src/http.zig +++ b/src/http.zig @@ -1556,6 +1556,7 @@ pub const RequestContext = struct { try ctx.flushHeaders(); // Output.prettyln("<r><green>101<r><d> Hot Module Reloading connected.<r>", .{}); // Output.flush(); + Analytics.Features.hot_module_reloading = true; var cmd: Api.WebsocketCommand = undefined; var msg: Api.WebsocketMessage = .{ @@ -2682,6 +2683,11 @@ pub const Server = struct { Output.flush(); + Analytics.Features.bun_bun = server.bundler.options.node_modules_bundle != null; + Analytics.Features.framework = server.bundler.options.framework != null; + Analytics.Features.filesystem_router = server.bundler.router != null; + Analytics.Features.bunjs = server.transform_options.node_modules_bundle_path_server != null; + var did_init = false; while (!did_init) { defer Output.flush(); @@ -2692,6 +2698,7 @@ pub const Server = struct { // We want to bind to the network socket as quickly as possible so that opening the URL works // We use a secondary loop so that we avoid the extra branch in a hot code path server.detectFastRefresh(); + Analytics.Features.fast_refresh = server.bundler.options.jsx.supports_fast_refresh; server.detectTSConfig(); try server.initWatcher(); did_init = true; @@ -2977,8 +2984,20 @@ pub const Server = struct { defer this.bundler.resetStore(); const dir_info = (this.bundler.resolver.readDirInfo(this.bundler.fs.top_level_dir) catch return) orelse return; + + if (dir_info.package_json) |pkg| { + Analytics.Features.macros = Analytics.Features.macros or pkg.macros.count() > 0; + Analytics.Features.always_bundle = pkg.always_bundle.len > 0; + Analytics.setProjectID(dir_info.abs_path, pkg.name); + } else { + Analytics.setProjectID(dir_info.abs_path, ""); + } + const tsconfig = dir_info.tsconfig_json orelse return; + Analytics.Features.tsconfig = true; + serve_as_package_path = tsconfig.base_url_for_paths.len > 0 or tsconfig.base_url.len > 0; + Analytics.Features.tsconfig_paths = tsconfig.paths.count() > 0; } pub var global_start_time: std.time.Timer = undefined; @@ -2999,6 +3018,8 @@ pub const Server = struct { server.bundler.configureLinker(); try server.bundler.configureRouter(true); + Analytics.Features.filesystem_router = server.bundler.router != null; + const public_folder_is_top_level = server.bundler.options.routes.static_dir_enabled and strings.eql( server.bundler.fs.top_level_dir, server.bundler.options.routes.static_dir, diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index f15fc08f5..d693bad68 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -16,6 +16,7 @@ const js_ast = @import("../../js_ast.zig"); const hash_map = @import("../../hash_map.zig"); const http = @import("../../http.zig"); const ImportKind = ast.ImportKind; +const Analytics = @import("../../analytics/analytics_thread.zig"); usingnamespace @import("./node_env_buf_map.zig"); usingnamespace @import("./base.zig"); usingnamespace @import("./webcore/response.zig"); @@ -579,6 +580,7 @@ pub const VirtualMachine = struct { pub fn enableMacroMode(this: *VirtualMachine) void { this.bundler.options.platform = .bun_macro; this.macro_mode = true; + Analytics.Features.macros = true; } pub fn disableMacroMode(this: *VirtualMachine) void { diff --git a/src/options.zig b/src/options.zig index d6c96034c..e734a59e6 100644 --- a/src/options.zig +++ b/src/options.zig @@ -14,6 +14,8 @@ const URL = @import("./query_string_map.zig").URL; const ConditionsMap = @import("./resolver/package_json.zig").ESModule.ConditionsMap; usingnamespace @import("global.zig"); +const Analytics = @import("./analytics/analytics_thread.zig"); + const DotEnv = @import("./env_loader.zig"); const assert = std.debug.assert; @@ -1034,6 +1036,9 @@ pub const BundleOptions = struct { .transform_options = transform, }; + Analytics.Features.define = Analytics.Features.define or transform.define != null; + Analytics.Features.loaders = Analytics.Features.loaders or transform.loaders != null; + if (transform.origin) |origin| { opts.origin = URL.parse(origin); } @@ -1324,6 +1329,15 @@ pub const BundleOptions = struct { opts.polyfill_node_globals = opts.platform != .node; + Analytics.Features.framework = Analytics.Features.framework or opts.framework != null; + Analytics.Features.filesystem_router = Analytics.Features.filesystem_router or opts.routes.routes_enabled; + Analytics.Features.origin = Analytics.Features.origin or transform.origin != null; + Analytics.Features.public_folder = Analytics.Features.public_folder or opts.routes.static_dir_enabled; + Analytics.Features.bun_bun = Analytics.Features.bun_bun or transform.node_modules_bundle_path != null; + Analytics.Features.bunjs = Analytics.Features.bunjs or transform.node_modules_bundle_path_server != null; + Analytics.Features.macros = Analytics.Features.macros or opts.platform == .bun_macro; + Analytics.Features.external = Analytics.Features.external or transform.external.len > 0; + Analytics.Features.single_page_app_routing = Analytics.Features.single_page_app_routing or opts.routes.single_page_app_routing; return opts; } }; |