analytics is good enough for now

This commit is contained in:
Jarred Sumner
2021-10-05 23:08:06 -07:00
parent bd14ad9e6e
commit 3b10cfae80
14 changed files with 474 additions and 179 deletions

View File

@@ -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

View File

@@ -1 +1 @@
32
33

View File

@@ -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,
});

17
misctools/features.zig Normal file
View File

@@ -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);
}

View File

@@ -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 fn decode(reader: anytype) anyerror!Platform {
var this = std.mem.zeroes(Platform);
this.os = try reader.readValue(OperatingSystem);
this.arch = try reader.readValue(Architecture);
this.version = try reader.readArray(u8);
return this;
}
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,
/// bundle_fail
bundle_fail,
/// http_start
http_start,
/// http_build
http_build,
/// bundle_start
bundle_start,
_,
pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void {
return try std.json.stringify(@tagName(self), opts, o);
}
};
pub const Uint64 = packed struct {
/// first
first: u32 = 0,
/// second
second: u32 = 0,
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 fn encode(this: *const @This(), writer: anytype) anyerror!void {
try writer.writeInt(this.first);
try writer.writeInt(this.second);
}
};
pub const EventListHeader = struct {
/// machine_id
machine_id: Uint64,
/// session_id
session_id: u32 = 0,
/// platform
platform: Platform,
/// build_id
build_id: u32 = 0,
/// session_length
session_length: 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.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 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 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);
}
};
};
pub const Architecture = enum(u8) {
_none,
/// x64
x64,
/// arm
arm,
_,
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,
/// arch
arch: Architecture,
/// version
version: []const u8,
pub fn decode(reader: anytype) anyerror!Platform {
var this = std.mem.zeroes(Platform);
this.os = try reader.readValue(OperatingSystem);
this.arch = try reader.readValue(Architecture);
this.version = try reader.readArray(u8);
return this;
}
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,
/// bundle_fail
bundle_fail,
/// http_start
http_start,
/// http_build
http_build,
/// bundle_start
bundle_start,
_,
pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void {
return try std.json.stringify(@tagName(self), opts, o);
}
};
pub const Uint64 = packed struct {
/// first
first: u32 = 0,
/// second
second: u32 = 0,
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 fn encode(this: *const @This(), writer: anytype) anyerror!void {
try writer.writeInt(this.first);
try writer.writeInt(this.second);
}
};
pub const EventListHeader = struct {
/// machine_id
machine_id: Uint64,
/// session_id
session_id: u32 = 0,
/// 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 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,

View File

@@ -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());
}

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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";

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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;
}
};