Files
bun.sh/src/bundler/entry_points.zig
Alistair Smith 247629aded cleaning
2025-09-22 20:24:20 -07:00

351 lines
14 KiB
Zig

pub const FallbackEntryPoint = struct {
code_buffer: [8192]u8 = undefined,
path_buffer: bun.PathBuffer = undefined,
source: logger.Source = undefined,
built_code: string = "",
pub fn generate(
entry: *FallbackEntryPoint,
input_path: string,
comptime TranspilerType: type,
transpiler: *TranspilerType,
) !void {
// This is *extremely* naive.
// The basic idea here is this:
// --
// import * as EntryPoint from 'entry-point';
// import boot from 'framework';
// boot(EntryPoint);
// --
// We go through the steps of printing the code -- only to then parse/transpile it because
// we want it to go through the linker and the rest of the transpilation process
const disable_css_imports = transpiler.options.framework.?.client_css_in_js != .auto_onimportcss;
var code: string = undefined;
if (disable_css_imports) {
const fmt =
\\globalThis.Bun_disableCSSImports = true;
\\import boot from '{s}';
\\boot(globalThis.__BUN_DATA__);
;
const args = .{
input_path,
};
const count = std.fmt.count(fmt, args);
if (count < entry.code_buffer.len) {
code = try std.fmt.bufPrint(&entry.code_buffer, fmt, args);
} else {
code = try std.fmt.allocPrint(transpiler.allocator, fmt, args);
}
} else {
const fmt =
\\import boot from '{s}';
\\boot(globalThis.__BUN_DATA__);
;
const args = .{
input_path,
};
const count = std.fmt.count(fmt, args);
if (count < entry.code_buffer.len) {
code = try std.fmt.bufPrint(&entry.code_buffer, fmt, args);
} else {
code = try std.fmt.allocPrint(transpiler.allocator, fmt, args);
}
}
entry.source = logger.Source.initPathString(input_path, code);
entry.source.path.namespace = "fallback-entry";
}
};
pub const ClientEntryPoint = struct {
code_buffer: [8192]u8 = undefined,
path_buffer: bun.PathBuffer = undefined,
source: logger.Source = undefined,
pub fn isEntryPointPath(extname: string) bool {
return strings.startsWith("entry.", extname);
}
pub fn generateEntryPointPath(outbuffer: []u8, original_path: Fs.PathName) string {
var joined_base_and_dir_parts = [_]string{ original_path.dir, original_path.base };
var generated_path = Fs.FileSystem.instance.absBuf(&joined_base_and_dir_parts, outbuffer);
bun.copy(u8, outbuffer[generated_path.len..], ".entry");
generated_path = outbuffer[0 .. generated_path.len + ".entry".len];
bun.copy(u8, outbuffer[generated_path.len..], original_path.ext);
return outbuffer[0 .. generated_path.len + original_path.ext.len];
}
pub fn decodeEntryPointPath(outbuffer: []u8, original_path: Fs.PathName) string {
var joined_base_and_dir_parts = [_]string{ original_path.dir, original_path.base };
const generated_path = Fs.FileSystem.instance.absBuf(&joined_base_and_dir_parts, outbuffer);
var original_ext = original_path.ext;
if (strings.indexOf(original_path.ext, "entry")) |entry_i| {
original_ext = original_path.ext[entry_i + "entry".len ..];
}
bun.copy(u8, outbuffer[generated_path.len..], original_ext);
return outbuffer[0 .. generated_path.len + original_ext.len];
}
pub fn generate(entry: *ClientEntryPoint, comptime TranspilerType: type, transpiler: *TranspilerType, original_path: Fs.PathName, client: string) !void {
// This is *extremely* naive.
// The basic idea here is this:
// --
// import * as EntryPoint from 'entry-point';
// import boot from 'framework';
// boot(EntryPoint);
// --
// We go through the steps of printing the code -- only to then parse/transpile it because
// we want it to go through the linker and the rest of the transpilation process
const dir_to_use: string = original_path.dirWithTrailingSlash();
const disable_css_imports = transpiler.options.framework.?.client_css_in_js != .auto_onimportcss;
var code: string = undefined;
if (disable_css_imports) {
code = try std.fmt.bufPrint(
&entry.code_buffer,
\\globalThis.Bun_disableCSSImports = true;
\\import boot from '{s}';
\\import * as EntryPoint from '{s}{s}';
\\boot(EntryPoint);
,
.{
client,
dir_to_use,
original_path.filename,
},
);
} else {
code = try std.fmt.bufPrint(
&entry.code_buffer,
\\import boot from '{s}';
\\if ('setLoaded' in boot) boot.setLoaded(loaded);
\\import * as EntryPoint from '{s}{s}';
\\boot(EntryPoint);
,
.{
client,
dir_to_use,
original_path.filename,
},
);
}
entry.source = logger.Source.initPathString(generateEntryPointPath(&entry.path_buffer, original_path), code);
entry.source.path.namespace = "client-entry";
}
};
pub const ServerEntryPoint = struct {
source: logger.Source = undefined,
pub fn generate(
entry: *ServerEntryPoint,
allocator: std.mem.Allocator,
is_hot_reload_enabled: bool,
path_to_use: string,
name: string,
) !void {
const code = brk: {
if (is_hot_reload_enabled) {
// Started development server
break :brk try std.fmt.allocPrint(
allocator,
\\// @bun
\\import * as start from '{}';
\\var hmrSymbol = Symbol("BunServerHMR");
\\var entryNamespace = start;
\\if (typeof entryNamespace?.then === 'function') {{
\\ entryNamespace = entryNamespace.then((entryNamespace) => {{
\\ var def = entryNamespace?.default;
\\ if (def && (typeof def.fetch === 'function' || def.app != undefined)) {{
\\ var server = globalThis[hmrSymbol];
\\ if (server) {{
\\ server.reload(def);
\\ console.debug(`Reloaded ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);
\\ }} else {{
\\ server = globalThis[hmrSymbol] = Bun.serve(def);
\\ console.debug(`Started ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);
\\ }}
\\ }}
\\ }}, reportError);
\\}} else if (typeof entryNamespace?.default?.fetch === 'function' || entryNamespace?.default?.app != undefined) {{
\\ var server = globalThis[hmrSymbol];
\\ if (server) {{
\\ server.reload(entryNamespace.default);
\\ console.debug(`Reloaded ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);
\\ }} else {{
\\ server = globalThis[hmrSymbol] = Bun.serve(entryNamespace.default);
\\ console.debug(`Started ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);
\\ }}
\\}}
\\
,
.{
strings.formatEscapes(path_to_use, .{ .quote_char = '\'' }),
},
);
}
break :brk try std.fmt.allocPrint(
allocator,
\\// @bun
\\import * as start from "{}";
\\var entryNamespace = start;
\\if (typeof entryNamespace?.then === 'function') {{
\\ entryNamespace = entryNamespace.then((entryNamespace) => {{
\\ if (typeof entryNamespace?.default?.fetch === 'function') {{
\\ const server = Bun.serve(entryNamespace.default);
\\ console.debug(`Started ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);
\\ }}
\\ }}, reportError);
\\}} else if (typeof entryNamespace?.default?.fetch === 'function' || entryNamespace?.default?.app != null) {{
\\ const server = Bun.serve(entryNamespace.default);
\\ console.debug(`Started ${{server.development ? 'development ' : ''}}server: ${{server.protocol}}://${{server.hostname}}:${{server.port}}`);
\\}}
\\
,
.{
strings.formatEscapes(path_to_use, .{ .quote_char = '"' }),
},
);
};
entry.source = logger.Source.initPathString(name, code);
entry.source.path.text = name;
entry.source.path.namespace = "server-entry";
}
};
// This is not very fast. The idea is: we want to generate a unique entry point
// per macro function export that registers the macro Registering the macro
// happens in VirtualMachine We "register" it which just marks the JSValue as
// protected. This is mostly a workaround for being unable to call ESM exported
// functions from C++. When that is resolved, we should remove this.
pub const MacroEntryPoint = struct {
code_buffer: [bun.MAX_PATH_BYTES * 2 + 500]u8 = undefined,
output_code_buffer: [bun.MAX_PATH_BYTES * 8 + 500]u8 = undefined,
source: logger.Source = undefined,
pub fn generateID(entry_path: string, function_name: string, buf: []u8, len: *u32) i32 {
var hasher = bun.Wyhash11.init(0);
hasher.update(js_ast.Macro.namespaceWithColon);
hasher.update(entry_path);
hasher.update(function_name);
const hash = hasher.final();
const fmt = bun.fmt.hexIntLower(hash);
const specifier = std.fmt.bufPrint(buf, js_ast.Macro.namespaceWithColon ++ "//{any}.js", .{fmt}) catch unreachable;
len.* = @as(u32, @truncate(specifier.len));
return generateIDFromSpecifier(specifier);
}
pub fn generateIDFromSpecifier(specifier: string) i32 {
return @as(i32, @bitCast(@as(u32, @truncate(bun.hash(specifier)))));
}
pub fn generate(
entry: *MacroEntryPoint,
_: *Transpiler,
import_path: Fs.PathName,
function_name: string,
macro_id: i32,
macro_label_: string,
) !void {
const dir_to_use: string = if (import_path.dir.len == 0) "" else import_path.dirWithTrailingSlash();
bun.copy(u8, &entry.code_buffer, macro_label_);
const macro_label = entry.code_buffer[0..macro_label_.len];
const code = brk: {
if (strings.eqlComptime(import_path.base, "bun")) {
break :brk try std.fmt.bufPrint(
entry.code_buffer[macro_label.len..],
\\//Auto-generated file
\\var Macros;
\\try {{
\\ Macros = globalThis.Bun;
\\}} catch (err) {{
\\ console.error("Error importing macro");
\\ throw err;
\\}}
\\const macro = Macros['{s}'];
\\if (!macro) {{
\\ throw new Error("Macro '{s}' not found in 'bun'");
\\}}
\\
\\Bun.registerMacro({d}, macro);
,
.{
function_name,
function_name,
macro_id,
},
);
}
break :brk try std.fmt.bufPrint(
entry.code_buffer[macro_label.len..],
\\//Auto-generated file
\\var Macros;
\\try {{
\\ Macros = await import('{s}{s}');
\\}} catch (err) {{
\\ console.error("Error importing macro");
\\ throw err;
\\}}
\\if (!('{s}' in Macros)) {{
\\ throw new Error("Macro '{s}' not found in '{s}{s}'");
\\}}
\\
\\Bun.registerMacro({d}, Macros['{s}']);
,
.{
bun.fmt.fmtPath(u8, dir_to_use, .{
.escape_backslashes = true,
}),
bun.fmt.fmtPath(u8, import_path.filename, .{
.escape_backslashes = true,
}),
function_name,
function_name,
bun.fmt.fmtPath(u8, dir_to_use, .{
.escape_backslashes = true,
}),
bun.fmt.fmtPath(u8, import_path.filename, .{
.escape_backslashes = true,
}),
macro_id,
function_name,
},
);
};
entry.source = logger.Source.initPathString(macro_label, code);
entry.source.path.text = macro_label;
entry.source.path.namespace = js_ast.Macro.namespace;
}
};
const string = []const u8;
const Fs = @import("../fs.zig");
const std = @import("std");
const bun = @import("bun");
const Transpiler = bun.Transpiler;
const js_ast = bun.ast;
const logger = bun.logger;
const strings = bun.strings;