aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-06-11 10:53:55 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-06-11 10:53:55 -0700
commitdc3309d130c171f75d4e416f3569faa9c985d123 (patch)
tree322a6ad3b280f47804f8e429b8089bee71777c80
parentd2e1c7955be8989eac8169ab46af93492e706d0b (diff)
downloadbun-dc3309d130c171f75d4e416f3569faa9c985d123.tar.gz
bun-dc3309d130c171f75d4e416f3569faa9c985d123.tar.zst
bun-dc3309d130c171f75d4e416f3569faa9c985d123.zip
alright basic stuff works now. still bugs with JS parser
Former-commit-id: a1dd2a2a32819c23541eed2acfd585e5fd6e5688
-rw-r--r--.vscode/launch.json14
-rw-r--r--src/api/schema.peechy10
-rw-r--r--src/bundler.zig58
-rw-r--r--src/http.zig80
-rw-r--r--src/js_parser/js_parser.zig33
-rw-r--r--src/js_printer.zig172
-rw-r--r--src/linker.zig52
-rw-r--r--src/node_module_bundle.zig4
-rw-r--r--src/runtime.js163
-rw-r--r--src/runtime.version2
-rw-r--r--src/runtime.zig13
11 files changed, 431 insertions, 170 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json
index c6dd132c6..414818c10 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -40,6 +40,20 @@
{
"type": "lldb",
"request": "launch",
+ "name": "Demo Lazy Build",
+ "program": "${workspaceFolder}/build/debug/macos-x86_64/esdev",
+ "args": [
+ "./src/index.tsx",
+ "--resolve=lazy",
+ "--public-url=http://localhost:9000/"
+ ],
+ "cwd": "${workspaceFolder}/demos/simple-react",
+ "console": "internalConsole"
+ },
+
+ {
+ "type": "lldb",
+ "request": "launch",
"name": "Demo Build",
"program": "${workspaceFolder}/build/debug/macos-x86_64/esdev",
"args": [
diff --git a/src/api/schema.peechy b/src/api/schema.peechy
index 69f904e41..87f44dbaf 100644
--- a/src/api/schema.peechy
+++ b/src/api/schema.peechy
@@ -47,14 +47,17 @@ struct StringPointer {
struct JavascriptBundledModule {
// package-relative path including file extension
StringPointer path;
+
+ // Source code
StringPointer code;
+
+ // index into JavascriptBundle.packages
uint32 package_id;
- // This is the export id, which is hash(path).hash(package.hash)
+ // The ESM export is this id ("$" + number.toString(16))
uint32 id;
// This lets us efficiently compare strings ignoring the extension
- // If we instead omit the extension
byte path_extname_length;
}
@@ -68,7 +71,7 @@ struct JavascriptBundledPackage {
}
struct JavascriptBundle {
- // This has to be sorted by ${package_prefix}path
+ // These are sorted alphabetically so you can do binary search
JavascriptBundledModule[] modules;
JavascriptBundledPackage[] packages;
@@ -78,6 +81,7 @@ struct JavascriptBundle {
// generated by hashing all ${name}@${version} in sorted order
byte[] app_package_json_dependencies_hash;
byte[] import_from_name;
+
// This is what StringPointer refers to
byte[] manifest_string;
}
diff --git a/src/bundler.zig b/src/bundler.zig
index bb10af3f4..e2905b443 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -288,7 +288,12 @@ pub fn NewBundler(cache_files: bool) type {
try generator.appendBytes(&initial_header);
// If we try to be smart and rely on .written, it turns out incorrect
const code_start_pos = try this.tmpfile.getPos();
- try generator.appendBytes(runtime.SourceContent ++ "\n\n");
+ if (isDebug) {
+ try generator.appendBytes(runtime.Runtime.sourceContent());
+ try generator.appendBytes("\n\n");
+ } else {
+ try generator.appendBytes(comptime runtime.Runtime.sourceContent() ++ "\n\n");
+ }
if (bundler.log.level == .verbose) {
bundler.resolver.debug_logs = try DebugLogs.init(allocator);
@@ -374,10 +379,10 @@ pub fn NewBundler(cache_files: bool) type {
javascript_bundle.modules = this.module_list.items;
javascript_bundle.packages = sorted_package_list;
javascript_bundle.manifest_string = this.header_string_buffer.list.items;
- var etag_bytes: [8]u8 = undefined;
const etag_u64 = hasher.final();
- std.mem.writeIntNative(u64, &etag_bytes, etag_u64);
- javascript_bundle.etag = &etag_bytes;
+ // We store the etag as a ascii hex encoded u64
+ // This is so we can send the bytes directly in the HTTP server instead of formatting it as hex each time.
+ javascript_bundle.etag = try std.fmt.allocPrint(allocator, "{x}", .{etag_u64});
javascript_bundle.generated_at = @truncate(u32, @intCast(u64, std.time.milliTimestamp()));
const basename = std.fs.path.basename(destination);
@@ -469,10 +474,10 @@ pub fn NewBundler(cache_files: bool) type {
defer this.scan_pass_result.reset();
defer this.bundler.resetStore();
var file_path = resolve.path_pair.primary;
+ var hasher = std.hash.Wyhash.init(0);
// If we're in a node_module, build that almost normally
if (resolve.is_from_node_modules) {
- var hasher = std.hash.Wyhash.init(0);
switch (loader) {
.jsx,
.tsx,
@@ -773,7 +778,42 @@ pub fn NewBundler(cache_files: bool) type {
continue;
}
+ // Always enqueue unwalked import paths, but if it's not a node_module, we don't care about the hash
try this.resolve_queue.writeItem(_resolved_import.*);
+
+ // trim node_modules/${package.name}/ from the string to save space
+ // This reduces metadata size by about 30% for a large-ish file
+ // A future optimization here could be to reuse the string from the original path
+ var node_module_root = strings.indexOf(resolved_import.path_pair.primary.text, node_module_root_string) orelse continue;
+
+ const package_json: *const PackageJSON = (resolved_import.package_json orelse (this.bundler.resolver.packageJSONForResolvedNodeModule(resolved_import) orelse {
+ this.log.addWarningFmt(
+ &source,
+ import_record.range.loc,
+ this.allocator,
+ "Failed to find package.json for \"{s}\". This will be unresolved and might break at runtime. If it's external, you could add it to the external list.",
+ .{
+ resolved_import.path_pair.primary.text,
+ },
+ ) catch {};
+ continue;
+ }));
+
+ // omit node_modules
+ node_module_root += node_module_root_string.len;
+ // // omit package name
+ node_module_root += package_json.name.len;
+ node_module_root += 1;
+
+ // It should be the first index, not the last to support bundling multiple of the same package
+ import_record.path = Fs.Path.init(
+ resolved_import.path_pair.primary.text[node_module_root..],
+ );
+
+ hasher = std.hash.Wyhash.init(0);
+ hasher.update(import_record.path.text);
+ hasher.update(std.mem.asBytes(&package_json.hash));
+ get_or_put_result.entry.value = @truncate(u32, hasher.final());
} else |err| {}
}
},
@@ -1019,6 +1059,7 @@ pub fn NewBundler(cache_files: bool) type {
var opts = js_parser.Parser.Options.init(jsx, loader);
opts.enable_bundling = bundler.options.node_modules_bundle != null;
opts.transform_require_to_import = true;
+ opts.can_import_from_bundle = bundler.options.node_modules_bundle != null;
const value = (bundler.resolver.caches.js.parse(allocator, opts, bundler.options.define, bundler.log, &source) catch null) orelse return null;
return ParseResult{
.ast = value,
@@ -1179,7 +1220,7 @@ pub fn NewBundler(cache_files: bool) type {
if (strings.eqlComptime(relative_path, "__runtime.js")) {
return ServeResult{
- .file = options.OutputFile.initBuf(runtime.SourceContent, "__runtime.js", .js),
+ .file = options.OutputFile.initBuf(runtime.Runtime.sourceContent(), "__runtime.js", .js),
.mime_type = MimeType.javascript,
};
}
@@ -1430,7 +1471,7 @@ pub fn NewBundler(cache_files: bool) type {
if (bundler.linker.any_needs_runtime) {
try bundler.output_files.append(
- options.OutputFile.initBuf(runtime.SourceContent, bundler.linker.runtime_source_path, .js),
+ options.OutputFile.initBuf(runtime.Runtime.sourceContent(), bundler.linker.runtime_source_path, .js),
);
}
@@ -1640,7 +1681,7 @@ pub const Transformer = struct {
jsx.parse = loader.isJSX();
var file_op = options.OutputFile.FileOperation.fromFile(file_to_write.handle, output_path.pretty);
- const parser_opts = js_parser.Parser.Options.init(jsx.*, loader);
+ var parser_opts = js_parser.Parser.Options.init(jsx.*, loader);
file_op.is_tmpdir = false;
output_file.value = .{ .move = file_op };
@@ -1650,6 +1691,7 @@ pub const Transformer = struct {
file_op.fd = file.handle;
var parser = try js_parser.Parser.init(parser_opts, log, _source, transformer.define, allocator);
+ parser_opts.can_import_from_bundle = false;
const result = try parser.parse();
const ast = result.ast;
diff --git a/src/http.zig b/src/http.zig
index 8e3d4b2c1..2d54a45c0 100644
--- a/src/http.zig
+++ b/src/http.zig
@@ -186,7 +186,7 @@ pub const RequestContext = struct {
const status_text = switch (code) {
200...299 => "OK",
300...399 => "=>",
- 400...499 => "UH",
+ 400...499 => "DID YOU KNOW YOU CAN MAKE THIS SAY WHATEVER YOU WANT",
500...599 => "ERR",
else => @compileError("Invalid code passed to printStatusLine"),
};
@@ -333,7 +333,39 @@ pub const RequestContext = struct {
ctx.done();
}
+ pub fn sendJSB(ctx: *RequestContext) !void {
+ const node_modules_bundle = ctx.bundler.options.node_modules_bundle orelse unreachable;
+ defer ctx.done();
+ ctx.appendHeader("ETag", node_modules_bundle.bundle.etag);
+ ctx.appendHeader("Content-Type", "text/javascript");
+ ctx.appendHeader("Cache-Control", "immutable, max-age=99999");
+
+ if (ctx.header("If-None-Match")) |etag_header| {
+ if (std.mem.eql(u8, node_modules_bundle.bundle.etag, etag_header.value)) {
+ try ctx.sendNotModified();
+ return;
+ }
+ }
+
+ const content_length = node_modules_bundle.container.code_length.? - node_modules_bundle.codeStartOffset();
+ try ctx.writeStatus(200);
+ try ctx.prepareToSendBody(content_length, false);
+
+ _ = try std.os.sendfile(
+ ctx.conn.client.socket.fd,
+ node_modules_bundle.fd,
+ node_modules_bundle.codeStartOffset(),
+ content_length,
+ &[_]std.os.iovec_const{},
+ &[_]std.os.iovec_const{},
+ 0,
+ );
+ }
+
pub fn handleGet(ctx: *RequestContext) !void {
+ if (strings.eqlComptime(ctx.url.extname, "jsb") and ctx.bundler.options.node_modules_bundle != null) {
+ return try ctx.sendJSB();
+ }
const result = try ctx.bundler.buildFile(
&ctx.log,
ctx.allocator,
@@ -596,34 +628,34 @@ pub const RequestContext = struct {
.copy, .move => |file| {
defer std.os.close(file.fd);
- if (result.mime_type.category != .html) {
- // hash(absolute_file_path, size, mtime)
- var weak_etag = std.hash.Wyhash.init(1);
- weak_etag_buffer[0] = 'W';
- weak_etag_buffer[1] = '/';
- weak_etag.update(result.file.input.text);
- std.mem.writeIntNative(u64, weak_etag_tmp_buffer[0..8], result.file.size);
- weak_etag.update(weak_etag_tmp_buffer[0..8]);
-
- if (result.file.mtime) |mtime| {
- std.mem.writeIntNative(i128, weak_etag_tmp_buffer[0..16], mtime);
- weak_etag.update(weak_etag_tmp_buffer[0..16]);
- }
+ // if (result.mime_type.category != .html) {
+ // hash(absolute_file_path, size, mtime)
+ var weak_etag = std.hash.Wyhash.init(1);
+ weak_etag_buffer[0] = 'W';
+ weak_etag_buffer[1] = '/';
+ weak_etag.update(result.file.input.text);
+ std.mem.writeIntNative(u64, weak_etag_tmp_buffer[0..8], result.file.size);
+ weak_etag.update(weak_etag_tmp_buffer[0..8]);
+
+ if (result.file.mtime) |mtime| {
+ std.mem.writeIntNative(i128, weak_etag_tmp_buffer[0..16], mtime);
+ weak_etag.update(weak_etag_tmp_buffer[0..16]);
+ }
- const etag_content_slice = std.fmt.bufPrintIntToSlice(weak_etag_buffer[2..], weak_etag.final(), 16, true, .{});
- const complete_weak_etag = weak_etag_buffer[0 .. etag_content_slice.len + 2];
+ const etag_content_slice = std.fmt.bufPrintIntToSlice(weak_etag_buffer[2..], weak_etag.final(), 16, true, .{});
+ const complete_weak_etag = weak_etag_buffer[0 .. etag_content_slice.len + 2];
- ctx.appendHeader("ETag", complete_weak_etag);
+ ctx.appendHeader("ETag", complete_weak_etag);
- if (ctx.header("If-None-Match")) |etag_header| {
- if (strings.eql(complete_weak_etag, etag_header.value)) {
- try ctx.sendNotModified();
- return;
- }
+ if (ctx.header("If-None-Match")) |etag_header| {
+ if (strings.eql(complete_weak_etag, etag_header.value)) {
+ try ctx.sendNotModified();
+ return;
}
- } else {
- ctx.appendHeader("Cache-Control", "no-cache");
}
+ // } else {
+ // ctx.appendHeader("Cache-Control", "no-cache");
+ // }
switch (result.file.size) {
0 => {
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig
index 0918298f8..23e9cfad0 100644
--- a/src/js_parser/js_parser.zig
+++ b/src/js_parser/js_parser.zig
@@ -1748,8 +1748,9 @@ pub const Parser = struct {
if (jsx_symbol.use_count_estimate > 0) {
require_call_args_base[require_call_args_i] = p.e(E.Identifier{ .ref = automatic_namespace_ref }, loc);
require_call_args_i += 1;
+
var require_call_args = require_call_args_base[0..require_call_args_i];
- var require_call = p.callRuntime(loc, "__require", require_call_args);
+ var require_call = p.callRequireOrBundledRequire(require_call_args);
declared_symbols[declared_symbols_i] = .{ .ref = p.jsx_runtime_ref, .is_top_level = true };
declared_symbols_i += 1;
@@ -1816,7 +1817,7 @@ pub const Parser = struct {
if (jsx_classic_symbol.use_count_estimate > 0) {
require_call_args_base[require_call_args_i] = p.e(E.Identifier{ .ref = classic_namespace_ref }, loc);
var require_call_args = require_call_args_base[require_call_args_i..];
- var require_call = p.callRuntime(loc, "__require", require_call_args);
+ var require_call = p.callRequireOrBundledRequire(require_call_args);
if (jsx_factory_symbol.use_count_estimate > 0) {
declared_symbols[declared_symbols_i] = .{ .ref = p.jsx_factory_ref, .is_top_level = true };
declared_symbols_i += 1;
@@ -1888,7 +1889,7 @@ pub const Parser = struct {
jsx_part_stmts[stmt_i] = p.s(S.Local{ .kind = .k_var, .decls = decls }, loc);
- before.append(js_ast.Part{
+ after.append(js_ast.Part{
.stmts = jsx_part_stmts,
.declared_symbols = declared_symbols,
.import_record_indices = import_records,
@@ -1946,7 +1947,7 @@ pub const Parser = struct {
};
}
- before.append(js_ast.Part{
+ after.append(js_ast.Part{
.stmts = p.cjs_import_stmts.items,
.declared_symbols = declared_symbols,
.import_record_indices = import_records,
@@ -1966,16 +1967,24 @@ pub const Parser = struct {
after_len +
parts_len,
);
+
+ var remaining_parts = _parts;
if (before_len > 0) {
- std.mem.copy(js_ast.Part, _parts, before.toOwnedSlice());
+ var parts_to_copy = before.toOwnedSlice();
+ std.mem.copy(js_ast.Part, remaining_parts, parts_to_copy);
+ remaining_parts = remaining_parts[parts_to_copy.len..];
}
if (parts_len > 0) {
- std.mem.copy(js_ast.Part, _parts[before_len .. before_len + parts_len], parts.toOwnedSlice());
+ var parts_to_copy = parts.toOwnedSlice();
+ std.mem.copy(js_ast.Part, remaining_parts, parts_to_copy);
+ remaining_parts = remaining_parts[parts_to_copy.len..];
}
if (after_len > 0) {
- std.mem.copy(js_ast.Part, _parts[before_len + parts_len .. _parts.len], after.toOwnedSlice());
+ var parts_to_copy = after.toOwnedSlice();
+ std.mem.copy(js_ast.Part, remaining_parts, parts_to_copy);
}
+
parts_slice = _parts;
} else {
after.deinit();
@@ -2641,6 +2650,16 @@ pub fn NewParser(
}
}
+ // If we're auto-importing JSX and it's bundled, we use the bundled version
+ // This means we need to transform from require(react) to react()
+ pub fn callRequireOrBundledRequire(p: *P, require_args: []Expr) Expr {
+ if (p.options.can_import_from_bundle) {
+ return p.e(E.Call{ .target = require_args[0] }, require_args[0].loc);
+ } else {
+ return p.callRuntime(require_args[0].loc, "__require", require_args);
+ }
+ }
+
pub fn recordExport(p: *P, loc: logger.Loc, alias: string, ref: Ref) !void {
if (p.named_exports.get(alias)) |name| {
// Duplicate exports are an error
diff --git a/src/js_printer.zig b/src/js_printer.zig
index 0e7ca5201..f45feea91 100644
--- a/src/js_printer.zig
+++ b/src/js_printer.zig
@@ -2145,8 +2145,7 @@ pub fn NewPrinter(
p.print("export ");
} else {
if (rewrite_esm_to_cjs) {
- p.printSymbol(p.options.runtime_imports.__export.?);
- p.print(".");
+ p.print("var ");
p.printSymbol(nameRef);
p.print(" = ");
}
@@ -2164,7 +2163,22 @@ pub fn NewPrinter(
p.printSpace();
p.printSymbol(nameRef);
p.printFunc(s.func);
- p.printNewline();
+
+ if (rewrite_esm_to_cjs and s.func.flags.is_export) {
+ p.printSemicolonAfterStatement();
+ } else {
+ p.printNewline();
+ }
+
+ if (rewrite_esm_to_cjs and s.func.flags.is_export) {
+ p.printIndent();
+ p.printSymbol(p.options.runtime_imports.__export.?);
+ p.print(".");
+ p.printSymbol(nameRef);
+ p.print(" = ");
+ p.printSymbol(nameRef);
+ p.printSemicolonAfterStatement();
+ }
},
.s_class => |s| {
// Give an extra newline for readaiblity
@@ -2174,23 +2188,40 @@ pub fn NewPrinter(
p.printIndent();
p.printSpaceBeforeIdentifier();
+ const nameRef = s.class.class_name.?.ref.?;
if (s.is_export) {
if (!rewrite_esm_to_cjs) {
p.print("export ");
}
if (rewrite_esm_to_cjs) {
- p.printSymbol(p.options.runtime_imports.__export.?);
- p.print(".");
- p.printSymbol(s.class.class_name.?.ref.?);
+ p.print("var ");
+ p.printSymbol(nameRef);
p.print(" = ");
}
}
p.print("class ");
- p.printSymbol(s.class.class_name.?.ref.?);
+ p.printSymbol(nameRef);
p.printClass(s.class);
- p.printNewline();
+
+ if (rewrite_esm_to_cjs and s.is_export) {
+ p.printSemicolonAfterStatement();
+ } else {
+ p.printNewline();
+ }
+
+ if (rewrite_esm_to_cjs) {
+ if (s.is_export) {
+ p.printIndent();
+ p.printSymbol(p.options.runtime_imports.__export.?);
+ p.print(".");
+ p.printSymbol(nameRef);
+ p.print(" = ");
+ p.printSymbol(nameRef);
+ p.printSemicolonAfterStatement();
+ }
+ }
},
.s_empty => |s| {
p.printIndent();
@@ -2206,10 +2237,7 @@ pub fn NewPrinter(
p.printIndent();
p.printSpaceBeforeIdentifier();
- if (rewrite_esm_to_cjs) {
- p.printSymbol(p.options.runtime_imports.__export.?);
- p.print(".default =");
- } else {
+ if (!rewrite_esm_to_cjs) {
p.print("export default");
}
@@ -2217,21 +2245,38 @@ pub fn NewPrinter(
switch (s.value) {
.expr => |expr| {
+ if (rewrite_esm_to_cjs) {
+ p.printSymbol(p.options.runtime_imports.__export.?);
+ p.print(".default = ");
+ }
+
// Functions and classes must be wrapped to avoid confusion with their statement forms
p.export_default_start = p.writer.written;
p.printExpr(expr, .comma, ExprFlag.None());
p.printSemicolonAfterStatement();
return;
},
+
.stmt => |s2| {
switch (s2.data) {
- .s_function => {
- const func = s2.getFunction();
+ .s_function => |func| {
p.printSpaceBeforeIdentifier();
+ if (rewrite_esm_to_cjs) {
+ if (func.func.name) |name| {
+ // p.print("var ");
+ // p.printSymbol(name.ref.?);
+ // p.print(" = ");
+ } else {
+ p.printSymbol(p.options.runtime_imports.__export.?);
+ p.print(".default = ");
+ }
+ }
+
if (func.func.flags.is_async) {
p.print("async ");
}
p.print("function");
+
if (func.func.flags.is_generator) {
p.print("*");
p.printSpace();
@@ -2240,22 +2285,65 @@ pub fn NewPrinter(
}
if (func.func.name) |name| {
- p.printSymbol(name.ref orelse Global.panic("Internal error: Expected func to have a name ref\n{s}", .{func}));
+ p.printSymbol(name.ref.?);
}
+
p.printFunc(func.func);
- p.printNewline();
+
+ if (rewrite_esm_to_cjs) {
+ p.printSemicolonAfterStatement();
+
+ if (rewrite_esm_to_cjs) {
+ if (func.func.name) |name| {
+ p.printIndent();
+ p.printSpaceBeforeIdentifier();
+ p.printSymbol(p.options.runtime_imports.__export.?);
+ p.print(".default = ");
+ p.printSymbol(name.ref.?);
+ p.printSemicolonAfterStatement();
+ }
+ }
+ } else {
+ p.printNewline();
+ }
},
- .s_class => {
- const class = s2.getClass();
+ .s_class => |class| {
p.printSpaceBeforeIdentifier();
+
+ if (rewrite_esm_to_cjs) {
+ if (class.class.class_name) |name| {
+ // p.print("var ");
+ // p.printSymbol(name.ref.?);
+ // p.print(" = ");
+ } else {
+ p.printSymbol(p.options.runtime_imports.__export.?);
+ p.print(".default = ");
+ }
+ }
+
if (class.class.class_name) |name| {
p.print("class ");
p.printSymbol(name.ref orelse Global.panic("Internal error: Expected class to have a name ref\n{s}", .{class}));
} else {
p.print("class");
}
+
p.printClass(class.class);
- p.printNewline();
+
+ if (rewrite_esm_to_cjs) {
+ p.printSemicolonAfterStatement();
+
+ if (class.class.class_name) |name| {
+ p.printIndent();
+ p.printSpaceBeforeIdentifier();
+ p.printSymbol(p.options.runtime_imports.__export.?);
+ p.print(".default = ");
+ p.printSymbol(name.ref.?);
+ p.printSemicolonAfterStatement();
+ }
+ } else {
+ p.printNewline();
+ }
},
else => {
Global.panic("Internal error: unexpected export default stmt data {s}", .{s});
@@ -2346,11 +2434,12 @@ pub fn NewPrinter(
const last = s.items.len - 1;
for (s.items) |item, i| {
const name = p.renamer.nameForSymbol(item.name.ref.?);
- p.printIdentifier(name);
+ p.printClauseAlias(item.alias);
+
if (!strings.eql(name, item.alias)) {
p.print(":");
p.printSpace();
- p.printClauseAlias(item.alias);
+ p.printIdentifier(name);
}
if (i < last) {
@@ -2434,6 +2523,7 @@ pub fn NewPrinter(
p.printSymbol(p.options.runtime_imports.lazy_export.?);
p.print("(");
p.printSymbol(p.options.runtime_imports.__export.?);
+ p.print(",");
// Avoid initializing an entire component library because you imported one icon
p.printLoadFromBundleWithoutCall(s.import_record_index);
@@ -2778,6 +2868,46 @@ pub fn NewPrinter(
return;
}
+ } else if (record.is_bundled) {
+ p.print("import {");
+ p.printLoadFromBundleWithoutCall(s.import_record_index);
+ p.print(" as ");
+ p.printSymbol(s.namespace_ref);
+ p.print("} from ");
+ p.printQuotedUTF8(record.path.text, false);
+ p.printSemicolonAfterStatement();
+
+ if (s.items.len > 0) {
+ p.printIndent();
+ p.printSpaceBeforeIdentifier();
+ p.print("var {");
+ for (s.items) |item, i| {
+ p.print(item.alias);
+ const name = p.renamer.nameForSymbol(item.name.ref.?);
+ if (!strings.eql(name, item.alias)) {
+ p.print(":");
+ p.printSymbol(item.name.ref.?);
+ }
+
+ if (i < s.items.len - 1) {
+ p.print(", ");
+ }
+ }
+ p.print("} = ");
+ p.printSymbol(s.namespace_ref);
+ p.print("()");
+ p.printSemicolonAfterStatement();
+ } else if (s.default_name) |default_name| {
+ p.printIndent();
+ p.printSpaceBeforeIdentifier();
+ p.print("var {default: ");
+ p.printSymbol(default_name.ref.?);
+ p.print("} = ");
+ p.printSymbol(s.namespace_ref);
+ p.print("()");
+ p.printSemicolonAfterStatement();
+ }
+ return;
}
p.print("import");
diff --git a/src/linker.zig b/src/linker.zig
index c8f792c4c..15b2e82e6 100644
--- a/src/linker.zig
+++ b/src/linker.zig
@@ -166,7 +166,7 @@ pub fn NewLinker(comptime BundlerType: type) type {
};
import_record.is_bundled = true;
- import_record.path.text = node_modules_bundle.str(found_module.path);
+ import_record.path.text = node_modules_bundle.bundle.import_from_name;
import_record.module_id = found_module.id;
needs_bundle = true;
continue;
@@ -257,6 +257,56 @@ pub fn NewLinker(comptime BundlerType: type) type {
.range = logger.Range{ .loc = logger.Loc{ .start = 0 }, .len = 0 },
};
}
+
+ const ImportStatementSorter = struct {
+ import_records: []ImportRecord,
+ pub fn lessThan(ctx: @This(), lhs: js_ast.Stmt, rhs: js_ast.Stmt) bool {
+ switch (lhs.data) {
+ .s_import => |li| {
+ switch (rhs.data) {
+ .s_import => |ri| {
+ const a = ctx.import_records[li.import_record_index];
+ const b = ctx.import_records[ri.import_record_index];
+ if (a.is_bundled and !b.is_bundled) {
+ return false;
+ } else {
+ return true;
+ }
+ },
+ else => {
+ return true;
+ },
+ }
+ },
+ else => {
+ switch (rhs.data) {
+ .s_import => |ri| {
+ const a = ctx.import_records[ri.import_record_index];
+ if (!a.is_bundled) {
+ return false;
+ } else {
+ return true;
+ }
+ },
+ else => {
+ return true;
+ },
+ }
+ },
+ }
+ }
+ };
+
+ // std.sort.sort(comptime T: type, items: []T, context: anytype, comptime lessThan: fn(context:@TypeOf(context), lhs:T, rhs:T)bool)
+
+ // Change the import order so that any bundled imports appear last
+ // This is to make it so the bundle (which should be quite large) is least likely to block rendering
+ if (needs_bundle) {
+ const sorter = ImportStatementSorter{ .import_records = result.ast.import_records };
+ for (result.ast.parts) |*part, i| {
+ std.sort.sort(js_ast.Stmt, part.stmts, sorter, ImportStatementSorter.lessThan);
+ }
+ }
}
const ImportPathsList = allocators.BSSStringList(512, 128);
diff --git a/src/node_module_bundle.zig b/src/node_module_bundle.zig
index ec85e4fb1..8642336e3 100644
--- a/src/node_module_bundle.zig
+++ b/src/node_module_bundle.zig
@@ -317,6 +317,10 @@ pub const NodeModuleBundle = struct {
Output.prettyln(indent ++ "<b>{d:6} packages", .{this.bundle.packages.len});
}
+ pub fn codeStartOffset(this: *const NodeModuleBundle) u32 {
+ return @intCast(u32, jsbundle_prefix.len);
+ }
+
pub fn printSummaryFromDisk(
comptime StreamType: type,
input: StreamType,
diff --git a/src/runtime.js b/src/runtime.js
index ddcdaddd7..f065a1c4e 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -9,21 +9,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
export var __markAsModule = (target) =>
__defProp(target, "__esModule", { value: true });
-export var __reExport = (target, module, desc) => {
- if ((module && typeof module === "object") || typeof module === "function") {
- for (let key of __getOwnPropNames(module))
- if (!__hasOwnProp.call(target, key) && key !== "default")
- __defProp(target, key, {
- get: () => module[key],
- enumerable:
- !(desc = __getOwnPropDesc(module, key)) || desc.enumerable,
- });
- }
- return target;
-};
-
-export var $$lz = (target, module, props) => {
- for (key in props) {
+// lazy require to prevent loading one icon from a design system
+export var $$lzy = (target, module, props) => {
+ for (let key in props) {
if (!__hasOwnProp.call(target, key) && key !== "default")
__defProp(target, key, {
get: () => module()[props[key]],
@@ -48,20 +36,52 @@ export var __toModule = (module) => {
);
};
-export var __commonJS =
- (cb, name, mod = {}) =>
- () => {
- return (
- mod,
- // friendly name for any errors while requiring
- (__name(cb, name),
- cb((mod = { exports: {} }), mod.exports),
- __name(mod, name),
- mod),
- // Don't add a name to exports incase it exports "name"
- mod.exports
- );
- };
+export var __commonJS = (cb, name) => {
+ var mod = {};
+ var has_run = false;
+
+ return {
+ [`#init_${name}`]() {
+ if (has_run) {
+ return mod.exports;
+ }
+ has_run = true;
+ __name(cb);
+
+ mod = { exports: {} };
+
+ cb(mod, mod.exports);
+
+ // If it's a default-only export, don't crash if they call .default on the module
+ if (
+ typeof mod.exports === "object" &&
+ "default" in mod.exports &&
+ Object.keys(mod.exports).len === 1
+ ) {
+ mod.exports = mod.exports.default;
+ Object.defineProperty(mod.exports, "default", {
+ get() {
+ return mod.exports;
+ },
+ enumerable: false,
+ });
+ // If it's a namespace export without .default, pretend .default is the same as mod.exports
+ } else if (
+ typeof mod.exports === "object" &&
+ !("default" in mod.exports)
+ ) {
+ Object.defineProperty(mod.exports, "default", {
+ get() {
+ return mod.exports;
+ },
+ enumerable: false,
+ });
+ }
+
+ return mod.exports;
+ },
+ }[`#init_${name}`];
+};
var require_cache = new WeakMap();
@@ -69,14 +89,14 @@ export var __SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
RequireFailedError: class {},
};
-__name(
- __SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED__MODULE_LOAD_CACHE.RequireFailedError,
- "RequireFailedError"
-);
-__name(
- __SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED__MODULE_LOAD_CACHE.Module,
- "Module"
-);
+// __name(
+// __SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED__MODULE_LOAD_CACHE.RequireFailedError,
+// "RequireFailedError"
+// );
+// __name(
+// __SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED__MODULE_LOAD_CACHE.Module,
+// "Module"
+// );
export var __require = (namespace) => {
var entry = require_cache.get(namespace);
@@ -125,73 +145,8 @@ if (
new Map();
}
-// Like require() but accepts:
-// - package_json_hash
-// - package_json_name
-// - module_path
-// This locks the require to a specific package version
-// This is also slightly faster to generate since we don't need to allocate additional strings
-// for import paths
-export var $$r = (package_json_hash, package_json_name, module_path) => {
- // Symbol is useful here because it gives us:
- // - A built-in "description" property for providing friendlier errors. Potentially shared across multiple tabs depending on engine implementaion, saving a little memory.
- // - Guranteed uniqueness, letting the JS engine deal with mapping import paths to unique identifiers instead of us
- // - Relatively cheap in-memory size, costs one machine word
- // - Shouldn't cause de-opts from mixing short strings and long strings
- // - auto-incrementing integer ID is an alternative, but a stable key means we don't worry about generating a manifest ahead of time and we don't worry about the order of the module declarations
- return __load(
- // The displayed description is everything after the first slash
- Symbol.for(`${package_json_hash}/${package_json_name}/${module_path}`)
- );
-};
-
-export var __load = (id) => {
- if (
- globalThis.__SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED__MODULE_LOAD_CACHE.has(
- id
- )
- ) {
- return globalThis.__SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED__MODULE_LOAD_CACHE.get(
- id
- );
- }
-
- const namespace =
- globalThis.__SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED__MODULE_REGISTRY.get(
- id
- );
-
- const target =
- Object.prototype.hasOwnProperty.call(namespace, "default") &&
- Object.keys(namespace).length === 1
- ? namespace["default"]
- : namespace;
-
- if (typeof target !== "function") {
- throw new __SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.RequireFailedError(
- `Couldn't find module "${namespace.description.substring(
- namespace.description.indexOf("/") + 1
- )}"`
- );
- }
-
- globalThis.__SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED__MODULE_LOAD_CACHE.set(
- id,
- target()
- );
-
- // It might be slightly slower to do this extra get, but only returning from the map
- // might be a better hint to a JS engine that "target" doesn't escape this function
- return globalThis.__SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED__MODULE_LOAD_CACHE.get(
- id
- );
-};
-
-export var $$m = (package_json_hash, package_json_name, module_path, cb) => {
- globalThis.__SPEEDY_INTERNAL_DO_NOT_USE_OR_YOU_WILL_BE_FIRED__MODULE_REGISTRY.set(
- Symbol.for(`${package_json_hash}/${package_json_name}/${module_path}`),
- __commonJS(cb, `${package_json_name}/${module_path}`)
- );
+export var $$m = (package_json_name, module_path, cb) => {
+ return __commonJS(cb, `${package_json_name}/${module_path}`);
};
export var __name = (target, name) => {
diff --git a/src/runtime.version b/src/runtime.version
index b48df3c1c..d7de1f38d 100644
--- a/src/runtime.version
+++ b/src/runtime.version
@@ -1 +1 @@
-9e9dfde98026fc70 \ No newline at end of file
+727d9b7284639b19 \ No newline at end of file
diff --git a/src/runtime.zig b/src/runtime.zig
index 5eceba772..f74ed8067 100644
--- a/src/runtime.zig
+++ b/src/runtime.zig
@@ -2,8 +2,19 @@ const options = @import("./options.zig");
usingnamespace @import("ast/base.zig");
usingnamespace @import("global.zig");
const std = @import("std");
-pub const SourceContent = @embedFile("./runtime.js");
+pub const ProdSourceContent = @embedFile("./runtime.js");
+
pub const Runtime = struct {
+ pub fn sourceContent() string {
+ if (isDebug) {
+ var runtime_path = std.fs.path.join(std.heap.c_allocator, &[_]string{ std.fs.path.dirname(@src().file).?, "runtime.js" }) catch unreachable;
+ const file = std.fs.openFileAbsolute(runtime_path, .{}) catch unreachable;
+ defer file.close();
+ return file.readToEndAlloc(std.heap.c_allocator, (file.stat() catch unreachable).size) catch unreachable;
+ } else {
+ return ProdSourceContent;
+ }
+ }
pub var version_hash = @embedFile("./runtime.version");
pub fn version() string {
return version_hash;