aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-07-28 20:56:29 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-07-28 20:56:29 -0700
commit4a8b2546526e97583a2743d17405f664cbf6a16e (patch)
tree7cfdef87ee13374afc908dd5b0860502036d1c70
parent86296897e55e0c80a3e93e27031e244525fb757c (diff)
downloadbun-4a8b2546526e97583a2743d17405f664cbf6a16e.tar.gz
bun-4a8b2546526e97583a2743d17405f664cbf6a16e.tar.zst
bun-4a8b2546526e97583a2743d17405f664cbf6a16e.zip
esmodules work?
Former-commit-id: 5cb5af4416c12518eb195d1b310990fc5c94d6c8
-rw-r--r--.vscode/settings.json2
-rw-r--r--Makefile2
-rw-r--r--src/bundler.zig22
-rw-r--r--src/fs.zig2
-rw-r--r--src/http.zig5
-rw-r--r--src/javascript/jsc/api/router.zig2
-rw-r--r--src/javascript/jsc/base.zig12
-rw-r--r--src/javascript/jsc/bindings/ZigGlobalObject.cpp8
-rw-r--r--src/javascript/jsc/bindings/bindings.cpp70
-rw-r--r--src/javascript/jsc/bindings/bindings.zig27
-rw-r--r--src/javascript/jsc/bindings/exports.zig12
-rw-r--r--src/javascript/jsc/bindings/headers-cpp.h2
-rw-r--r--src/javascript/jsc/bindings/headers.h4
-rw-r--r--src/javascript/jsc/bindings/headers.zig2
-rw-r--r--src/javascript/jsc/javascript.zig182
-rw-r--r--src/js_parser/js_parser.zig98
-rw-r--r--src/js_printer.zig40
-rw-r--r--src/linker.zig35
-rw-r--r--src/main_javascript.zig19
-rw-r--r--src/options.zig4
-rw-r--r--src/runtime.zig2
-rw-r--r--src/string_immutable.zig24
-rw-r--r--src/test/fixtures/console.log.js3
-rw-r--r--src/test/fixtures/hello.js1
24 files changed, 377 insertions, 203 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 7a5abcab1..f2b65071c 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,6 @@
{
"git.autoRepositoryDetection": "openEditors",
- "search.quickOpen.includeSymbols": true,
+ "search.quickOpen.includeSymbols": false,
"search.seedWithNearestWord": true,
"search.smartCase": true,
"search.exclude": {
diff --git a/Makefile b/Makefile
index 1902e2d9a..35f5feb0e 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@ CLANG_FLAGS = -Isrc/JavaScript/jsc/WebKit/WebKitBuild/Release/JavaScriptCore/Pri
-DNDEBUG=1 \
-DNOMINMAX \
-DIS_BUILD \
- -O1 \
+ -O3 \
-g \
-DENABLE_INSPECTOR_ALTERNATE_DISPATCHERS=0 \
-DBUILDING_JSCONLY__ \
diff --git a/src/bundler.zig b/src/bundler.zig
index 9cbb97626..b31fb8308 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -1032,7 +1032,7 @@ pub fn NewBundler(cache_files: bool) type {
};
};
- try bundler.linker.link(file_path, &result, import_path_format);
+ try bundler.linker.link(file_path, &result, import_path_format, false);
return BuildResolveResultPair{
.written = try bundler.print(
@@ -1099,6 +1099,7 @@ pub fn NewBundler(cache_files: bool) type {
file_path,
&result,
import_path_format,
+ false,
);
output_file.size = try bundler.print(
@@ -1301,22 +1302,6 @@ pub fn NewBundler(cache_files: bool) type {
Linker,
&bundler.linker,
),
- .speedy => try js_printer.printSpeedyCJS(
- Writer,
- writer,
- ast,
- js_ast.Symbol.Map.initList(symbols),
- &result.source,
- false,
- js_printer.Options{
- .to_module_ref = Ref.RuntimeRef,
- .externals = ast.externals,
- .runtime_imports = ast.runtime_imports,
- .require_ref = ast.require_ref,
- },
- Linker,
- &bundler.linker,
- ),
};
}
@@ -1360,8 +1345,7 @@ pub fn NewBundler(cache_files: bool) type {
jsx.parse = loader.isJSX();
var opts = js_parser.Parser.Options.init(jsx, loader);
opts.enable_bundling = false;
- opts.transform_require_to_import = !bundler.options.platform.implementsRequire();
- opts.force_commonjs = bundler.options.platform == .speedy;
+ opts.transform_require_to_import = true;
opts.can_import_from_bundle = bundler.options.node_modules_bundle != null;
opts.features.hot_module_reloading = bundler.options.hot_module_reloading and bundler.options.platform != .speedy;
opts.features.react_fast_refresh = opts.features.hot_module_reloading and jsx.parse and bundler.options.jsx.supports_fast_refresh;
diff --git a/src/fs.zig b/src/fs.zig
index a0e6e8f42..ba7eaf32f 100644
--- a/src/fs.zig
+++ b/src/fs.zig
@@ -885,7 +885,7 @@ pub const PathName = struct {
// so if dir does not have a trailing slash, but is spaced one apart from the basename
// we can assume there is a trailing slash there
// so we extend the original slice's length by one
- return this.dir.ptr[0 .. this.dir.len + @intCast(
+ return if (this.dir.len == 0) "./" else this.dir.ptr[0 .. this.dir.len + @intCast(
usize,
@boolToInt(
this.dir[this.dir.len - 1] != std.fs.path.sep_posix and (@ptrToInt(this.dir.ptr) + this.dir.len + 1) == @ptrToInt(this.base.ptr),
diff --git a/src/http.zig b/src/http.zig
index 780cce9fe..16daae48e 100644
--- a/src/http.zig
+++ b/src/http.zig
@@ -529,6 +529,7 @@ pub const RequestContext = struct {
Fs.Path.init(file_path_str),
&parse_result,
.absolute_url,
+ false,
);
var written = this.bundler.print(parse_result, @TypeOf(&this.printer), &this.printer, .esm) catch |err| {
@@ -691,8 +692,8 @@ pub const RequestContext = struct {
boot,
.entry_point,
);
- JavaScript.VirtualMachine.instance = vm;
- javascript_vm = JavaScript.VirtualMachine.instance;
+ JavaScript.VirtualMachine.vm = vm;
+ javascript_vm = JavaScript.VirtualMachine.vm;
var exception: js.JSValueRef = null;
var load_result = try JavaScript.Module.loadFromResolveResult(vm, vm.ctx, resolved_entry_point, &exception);
diff --git a/src/javascript/jsc/api/router.zig b/src/javascript/jsc/api/router.zig
index 84dad71ef..5c3559636 100644
--- a/src/javascript/jsc/api/router.zig
+++ b/src/javascript/jsc/api/router.zig
@@ -20,7 +20,7 @@ pub fn importRoute(
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSObjectRef {
- return JavaScript.VirtualMachine.instance.require(
+ return JavaScript.VirtualMachine.vm.require(
ctx,
std.fs.path.dirname(this.route.file_path).?,
this.route.file_path,
diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig
index 36567000a..7bd9b96a4 100644
--- a/src/javascript/jsc/base.zig
+++ b/src/javascript/jsc/base.zig
@@ -1296,6 +1296,18 @@ pub fn NewClass(
def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor.rfn).rfn;
} else if (comptime strings.eqlComptime(function_names[i], "finalize")) {
def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize.rfn).rfn;
+ } else if (comptime strings.eqlComptime(function_names[i], "callAsFunction")) {
+ const ctxfn = @field(staticFunctions, function_names[i]).rfn;
+ const Func: std.builtin.TypeInfo.Fn = @typeInfo(@TypeOf(ctxfn)).Fn;
+
+ const PointerType = std.meta.Child(Func.args[0].arg_type.?);
+
+ var callback = if (Func.calling_convention == .C) ctxfn else To.JS.Callback(
+ PointerType,
+ ctxfn,
+ ).rfn;
+
+ def.callAsFunction = callback;
} else {
const ctxfn = @field(staticFunctions, function_names[i]).rfn;
const Func: std.builtin.TypeInfo.Fn = @typeInfo(@TypeOf(ctxfn)).Fn;
diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
index ce66998ec..26ad666ba 100644
--- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp
+++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
@@ -82,7 +82,7 @@ const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
&shouldInterruptScript,
&javaScriptRuntimeFlags,
nullptr, // queueTaskToEventLoop
-nullptr, // &shouldInterruptScriptBeforeTimeout,
+ nullptr, // &shouldInterruptScriptBeforeTimeout,
&moduleLoaderImportModule, // moduleLoaderImportModule
&moduleLoaderResolve, // moduleLoaderResolve
&moduleLoaderFetch, // moduleLoaderFetch
@@ -127,7 +127,6 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count) {
}
this->addStaticGlobals(extraStaticGlobals.data(), count);
extraStaticGlobals.releaseBuffer();
-
}
JSC::Identifier GlobalObject::moduleLoaderResolve(
@@ -140,11 +139,11 @@ JSC::Identifier GlobalObject::moduleLoaderResolve(
auto res = Zig__GlobalObject__resolve(
globalObject,
toZigString(key, globalObject),
- toZigString(referrer, globalObject)
+ referrer.isString() ? toZigString(referrer, globalObject) : ZigStringEmpty
);
if (res.success) {
- return toIdentifier(res.result.value, globalObject);
+ return toIdentifier(res.result.value, globalObject);
} else {
auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
throwException(scope, res.result.err.message, globalObject);
@@ -215,6 +214,7 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalOb
scope.release();
promise->resolve(globalObject, sourceCode);
+ globalObject->vm().drainMicrotasks();
return promise;
}
diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp
index c18638ba3..866c00540 100644
--- a/src/javascript/jsc/bindings/bindings.cpp
+++ b/src/javascript/jsc/bindings/bindings.cpp
@@ -116,8 +116,74 @@ JSC__JSInternalPromise* JSC__JSModuleLoader__importModule(JSC__JSGlobalObject* a
JSC__JSValue JSC__JSModuleLoader__linkAndEvaluateModule(JSC__JSGlobalObject* arg0, const JSC__Identifier* arg1) {
return JSC::JSValue::encode(JSC::linkAndEvaluateModule(arg0, *arg1, JSC::JSValue{}));
}
-JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* arg0, const WTF__String* arg1) {
- return JSC::loadAndEvaluateModule(arg0, *arg1, JSC::JSValue{}, JSC::JSValue{});
+
+static JSC::Identifier jsValueToModuleKey(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSValue value)
+{
+ if (value.isSymbol())
+ return JSC::Identifier::fromUid(JSC::jsCast<JSC::Symbol*>(value)->privateName());
+ return JSC::asString(value)->toIdentifier(lexicalGlobalObject);
+}
+
+static JSC::JSValue doLink(JSC__JSGlobalObject* globalObject, JSC::JSValue moduleKeyValue) {
+ JSC::VM& vm = globalObject->vm();
+ JSC::JSLockHolder lock { vm };
+ if (!(moduleKeyValue.isString() || moduleKeyValue.isSymbol())) {
+ return JSC::jsUndefined();
+ }
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSC::Identifier moduleKey = jsValueToModuleKey(globalObject, moduleKeyValue);
+ RETURN_IF_EXCEPTION(scope, { });
+
+ return JSC::linkAndEvaluateModule(globalObject, moduleKey, JSC::JSValue());
+}
+
+static JSC::JSNativeStdFunction* resolverFunction;
+static JSC::JSNativeStdFunction* rejecterFunction;
+static bool resolverFunctionInitialized = false;
+
+static JSC::EncodedJSValue resolverFunctionCallback(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) {
+ return JSC::JSValue::encode(doLink(globalObject, callFrame->argument(0)));
+}
+
+static JSC::EncodedJSValue rejecterFunctionCallback(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) {
+ return JSC::JSValue::encode(callFrame->argument(0));
+}
+
+
+JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* globalObject, ZigString arg1) {
+ globalObject->vm().drainMicrotasks();
+ auto name = Zig::toString(arg1);
+ name.impl()->ref();
+
+ auto* promise = JSC::loadAndEvaluateModule(
+ globalObject,
+ name,
+ JSC::jsUndefined(),
+ JSC::jsUndefined()
+ );
+
+ if (!resolverFunctionInitialized) {
+ resolverFunction = JSC::JSNativeStdFunction::create(globalObject->vm(), globalObject, 1, String(), resolverFunctionCallback);
+ rejecterFunction = JSC::JSNativeStdFunction::create(globalObject->vm(), globalObject, 1, String(), rejecterFunctionCallback);
+ resolverFunctionInitialized = true;
+ JSC::gcProtect(resolverFunction);
+ JSC::gcProtect(rejecterFunction);
+ }
+
+ auto result = promise->then(globalObject, resolverFunction, rejecterFunction);
+ globalObject->vm().drainMicrotasks();
+
+
+ // if (promise->status(globalObject->vm()) == JSC::JSPromise::Status::Fulfilled) {
+ // return reinterpret_cast<JSC::JSInternalPromise*>(
+ // JSC::JSInternalPromise::resolvedPromise(
+ // globalObject,
+ // doLink(globalObject, promise->result(globalObject->vm()))
+ // )
+ // );
+ // }
+
+ return result;
}
JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModuleEntryPoint(JSC__JSGlobalObject* arg0, const JSC__SourceCode* arg1) {
return JSC::loadAndEvaluateModule(arg0, *arg1, JSC::JSValue{});
diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig
index 0e585d49f..09f40d475 100644
--- a/src/javascript/jsc/bindings/bindings.zig
+++ b/src/javascript/jsc/bindings/bindings.zig
@@ -174,46 +174,46 @@ pub fn NewGlobalObject(comptime Type: type) type {
return struct {
pub fn import(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableZigString {
if (comptime @hasDecl(Type, "import")) {
- return @call(.{ .modifier = .always_inline }, Interface.import, .{ global, specifier, source });
+ return @call(.{ .modifier = .always_inline }, Type.import, .{ global, specifier, source });
}
return ErrorableZigString.err(error.ImportFailed, "Import not implemented");
}
pub fn resolve(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableZigString {
if (comptime @hasDecl(Type, "resolve")) {
- return @call(.{ .modifier = .always_inline }, Interface.resolve, .{ global, specifier, source });
+ return @call(.{ .modifier = .always_inline }, Type.resolve, .{ global, specifier, source });
}
return ErrorableZigString.err(error.ResolveFailed, "resolve not implemented");
}
pub fn fetch(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableZigString {
if (comptime @hasDecl(Type, "fetch")) {
- return @call(.{ .modifier = .always_inline }, Interface.fetch, .{ global, specifier, source });
+ return @call(.{ .modifier = .always_inline }, Type.fetch, .{ global, specifier, source });
}
return ErrorableZigString.err(error.FetchFailed, "Module fetch not implemented");
}
pub fn promiseRejectionTracker(global: *JSGlobalObject, promise: *JSPromise, rejection: JSPromiseRejectionOperation) callconv(.C) JSValue {
if (comptime @hasDecl(Type, "promiseRejectionTracker")) {
- return @call(.{ .modifier = .always_inline }, Interface.promiseRejectionTracker, .{ global, promise, rejection });
+ return @call(.{ .modifier = .always_inline }, Type.promiseRejectionTracker, .{ global, promise, rejection });
}
return JSValue.jsUndefined();
}
pub fn reportUncaughtException(global: *JSGlobalObject, exception: *Exception) callconv(.C) JSValue {
if (comptime @hasDecl(Type, "reportUncaughtException")) {
- return @call(.{ .modifier = .always_inline }, Interface.reportUncaughtException, .{ global, exception });
+ return @call(.{ .modifier = .always_inline }, Type.reportUncaughtException, .{ global, exception });
}
return JSValue.jsUndefined();
}
pub fn createImportMetaProperties(global: *JSGlobalObject, loader: *JSModuleLoader, obj: JSValue, record: *JSModuleRecord, specifier: JSValue) callconv(.C) JSValue {
if (comptime @hasDecl(Type, "createImportMetaProperties")) {
- return @call(.{ .modifier = .always_inline }, Interface.createImportMetaProperties, .{ global, loader, obj, record, specifier });
+ return @call(.{ .modifier = .always_inline }, Type.createImportMetaProperties, .{ global, loader, obj, record, specifier });
}
return JSValue.jsUndefined();
}
pub fn onCrash() callconv(.C) void {
if (comptime @hasDecl(Type, "onCrash")) {
- return @call(.{ .modifier = .always_inline }, Interface.onCrash, .{});
+ return @call(.{ .modifier = .always_inline }, Type.onCrash, .{});
}
Global.panic("C++ crashed :(", .{});
@@ -256,7 +256,7 @@ pub const JSModuleLoader = extern struct {
});
}
- pub fn loadAndEvaluateModule(globalObject: *JSGlobalObject, module_name: *const String) *JSInternalPromise {
+ pub fn loadAndEvaluateModule(globalObject: *JSGlobalObject, module_name: ZigString) *JSInternalPromise {
return shim.cppFn("loadAndEvaluateModule", .{
globalObject,
module_name,
@@ -393,13 +393,7 @@ pub const JSInternalPromise = extern struct {
pub const name = "JSC::JSInternalPromise";
pub const namespace = "JSC";
- pub const Status = enum(u32) {
- Pending = 0, // Making this as 0, so that, we can change the status from Pending to others without masking.
- Fulfilled = 1,
- Rejected = 2,
- };
-
- pub fn status(this: *const JSInternalPromise, vm: *VM) Status {
+ pub fn status(this: *const JSInternalPromise, vm: *VM) JSPromise.Status {
return shim.cppFn("status", .{ this, vm });
}
pub fn result(this: *const JSInternalPromise, vm: *VM) JSValue {
@@ -1356,8 +1350,9 @@ pub const VM = extern struct {
pub fn throwError(vm: *VM, global_object: *JSGlobalObject, scope: *ThrowScope, message: [*]const u8, len: usize) bool {
return cppFn("throwError", .{
vm,
- scope,
+
global_object,
+ scope,
message,
len,
diff --git a/src/javascript/jsc/bindings/exports.zig b/src/javascript/jsc/bindings/exports.zig
index 8c94f1577..82c9e8dd0 100644
--- a/src/javascript/jsc/bindings/exports.zig
+++ b/src/javascript/jsc/bindings/exports.zig
@@ -2,7 +2,7 @@ usingnamespace @import("./bindings.zig");
usingnamespace @import("./shared.zig");
const Fs = @import("../../../fs.zig");
const CAPI = @import("../JavaScriptCore.zig");
-
+const JS = @import("../javascript.zig");
const Handler = struct {
pub export fn global_signal_handler_fn(sig: i32, info: *const std.os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) void {
Global.panic("C++ Crash!!", .{});
@@ -16,7 +16,7 @@ pub const ZigGlobalObject = extern struct {
pub const name = "Zig::GlobalObject";
pub const include = "\"ZigGlobalObject.h\"";
pub const namespace = shim.namespace;
- pub const Interface: type = NewGlobalObject(std.meta.globalOption("JavaScript", type) orelse struct {});
+ pub const Interface: type = NewGlobalObject(std.meta.globalOption("JavaScriptVirtualMachine", type) orelse struct {});
pub var sigaction: std.os.Sigaction = undefined;
pub var sigaction_installed = false;
@@ -149,7 +149,7 @@ pub fn Errorable(comptime Type: type) type {
threadlocal var err_buf: [4096]u8 = undefined;
pub fn errFmt(code: anyerror, comptime fmt: []const u8, args: anytype) @This() {
- const message = std.fmt.bufPrint(&err_buf, fmt, args) catch @errorName(code);
+ const message = std.fmt.bufPrint(&err_buf, fmt, args) catch @as([]const u8, @errorName(code)[0..]);
return @call(.{ .modifier = .always_inline }, err, .{ code, message });
}
@@ -207,15 +207,15 @@ pub const ZigConsoleClient = struct {
vals: [*]JSValue,
len: usize,
) callconv(.C) void {
- var console = zigCast(ZigConsoleClient, console_);
+ var console = JS.VirtualMachine.vm.console;
var i: usize = 0;
var writer = console.writer;
if (len == 1) {
var str = vals[0].toWTFString(global);
var slice = str.slice();
- _ = writer.unbuffered_writer.write(slice) catch 0;
- if (slice.len > 0 and slice[slice.len - 1] != '\n') {
+ var written = writer.unbuffered_writer.write(slice) catch 0;
+ if (written > 0 and slice[slice.len - 1] != '\n') {
_ = writer.unbuffered_writer.write("\n") catch 0;
}
return;
diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h
index 85b09d7ff..46486557a 100644
--- a/src/javascript/jsc/bindings/headers-cpp.h
+++ b/src/javascript/jsc/bindings/headers-cpp.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1627506357
+//-- AUTOGENERATED FILE -- 1627515944
#pragma once
#include <stddef.h>
diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h
index 08851f3e1..944ceffb5 100644
--- a/src/javascript/jsc/bindings/headers.h
+++ b/src/javascript/jsc/bindings/headers.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1627506357
+//-- AUTOGENERATED FILE -- 1627515944
#pragma once
#include <stddef.h>
@@ -257,7 +257,7 @@ CPP_DECL bool JSC__JSModuleLoader__checkSyntax(JSC__JSGlobalObject* arg0, const
CPP_DECL JSC__JSValue JSC__JSModuleLoader__evaluate(JSC__JSGlobalObject* arg0, const unsigned char* arg1, size_t arg2, const unsigned char* arg3, size_t arg4, JSC__JSValue JSValue5, JSC__JSValue* arg6);
CPP_DECL JSC__JSInternalPromise* JSC__JSModuleLoader__importModule(JSC__JSGlobalObject* arg0, const JSC__Identifier* arg1);
CPP_DECL JSC__JSValue JSC__JSModuleLoader__linkAndEvaluateModule(JSC__JSGlobalObject* arg0, const JSC__Identifier* arg1);
-CPP_DECL JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* arg0, const WTF__String* arg1);
+CPP_DECL JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* arg0, ZigString arg1);
CPP_DECL JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModuleEntryPoint(JSC__JSGlobalObject* arg0, const JSC__SourceCode* arg1);
#pragma mark - JSC::JSModuleRecord
diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig
index 90e142bd5..783d9c82d 100644
--- a/src/javascript/jsc/bindings/headers.zig
+++ b/src/javascript/jsc/bindings/headers.zig
@@ -119,7 +119,7 @@ pub extern fn JSC__JSModuleLoader__checkSyntax(arg0: [*c]JSC__JSGlobalObject, ar
pub extern fn JSC__JSModuleLoader__evaluate(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]const u8, arg2: usize, arg3: [*c]const u8, arg4: usize, JSValue5: JSC__JSValue, arg6: [*c]JSC__JSValue) JSC__JSValue;
pub extern fn JSC__JSModuleLoader__importModule(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]const JSC__Identifier) [*c]JSC__JSInternalPromise;
pub extern fn JSC__JSModuleLoader__linkAndEvaluateModule(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]const JSC__Identifier) JSC__JSValue;
-pub extern fn JSC__JSModuleLoader__loadAndEvaluateModule(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]const WTF__String) [*c]JSC__JSInternalPromise;
+pub extern fn JSC__JSModuleLoader__loadAndEvaluateModule(arg0: [*c]JSC__JSGlobalObject, arg1: ZigString) [*c]JSC__JSInternalPromise;
pub extern fn JSC__JSModuleLoader__loadAndEvaluateModuleEntryPoint(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]const JSC__SourceCode) [*c]JSC__JSInternalPromise;
pub extern fn JSC__JSModuleRecord__sourceCode(arg0: [*c]JSC__JSModuleRecord) bJSC__SourceCode;
pub extern fn JSC__JSPromise__isHandled(arg0: [*c]const JSC__JSPromise, arg1: [*c]JSC__VM) bool;
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 51a287b27..9850f0c6a 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -19,10 +19,13 @@ usingnamespace @import("./config.zig");
usingnamespace @import("./bindings/exports.zig");
usingnamespace @import("./bindings/bindings.zig");
+const Runtime = @import("../../runtime.zig");
+
pub const GlobalClasses = [_]type{
Request.Class,
Response.Class,
Headers.Class,
+ EventListenerMixin.addEventListener(VirtualMachine),
};
pub const Module = struct {
@@ -39,9 +42,11 @@ pub const VirtualMachine = struct {
node_modules: ?*NodeModuleBundle = null,
bundler: Bundler,
watcher: ?*http.Watcher = null,
- console: ZigConsoleClient,
+ console: *ZigConsoleClient,
require_cache: RequireCacheType,
log: *logger.Log,
+ event_listeners: EventListenerMixin.Map,
+ pub threadlocal var vm_loaded = false;
pub threadlocal var vm: *VirtualMachine = undefined;
pub fn init(
@@ -58,21 +63,27 @@ pub const VirtualMachine = struct {
}
vm = try allocator.create(VirtualMachine);
+ var console = try allocator.create(ZigConsoleClient);
+ console.* = ZigConsoleClient.init(Output.errorWriter(), Output.writer());
+
vm.* = VirtualMachine{
.global = undefined,
.allocator = allocator,
.require_cache = RequireCacheType.init(allocator),
+ .event_listeners = EventListenerMixin.Map.init(allocator),
.bundler = try Bundler.init(
allocator,
log,
try configureTransformOptionsForSpeedy(allocator, _args),
existing_bundle,
),
- .console = ZigConsoleClient.init(Output.errorWriter(), Output.writer()),
+ .console = console,
.node_modules = existing_bundle,
.log = log,
};
+ vm.bundler.configureLinker();
+
var global_classes: [GlobalClasses.len]js.JSClassRef = undefined;
inline for (GlobalClasses) |Class, i| {
global_classes[i] = Class.get().*;
@@ -80,11 +91,150 @@ pub const VirtualMachine = struct {
vm.global = ZigGlobalObject.create(
&global_classes,
@intCast(i32, global_classes.len),
- &vm.console,
+ vm.console,
);
+ vm_loaded = true;
return vm;
}
+
+ // dynamic import
+ // pub fn import(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableZigString {
+
+ // }
+
+ threadlocal var source_code_printer: js_printer.BufferPrinter = undefined;
+ threadlocal var source_code_printer_loaded: bool = false;
+
+ inline fn _fetch(global: *JSGlobalObject, specifier: string, source: string) !string {
+ std.debug.assert(VirtualMachine.vm_loaded);
+ std.debug.assert(VirtualMachine.vm.global == global);
+
+ if (strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) {
+ return Runtime.Runtime.sourceContent();
+ }
+
+ const result = vm.bundler.resolve_results.get(specifier) orelse return error.MissingResolveResult;
+ const path = result.path_pair.primary;
+ const loader = vm.bundler.options.loaders.get(path.name.ext) orelse .file;
+
+ switch (loader) {
+ .js, .jsx, .ts, .tsx, .json => {
+ vm.bundler.resetStore();
+ const hash = http.Watcher.getHash(path.text);
+
+ var fd: ?StoredFileDescriptorType = null;
+
+ if (vm.watcher) |watcher| {
+ if (watcher.indexOf(hash)) |index| {
+ fd = watcher.watchlist.items(.fd)[index];
+ }
+ }
+
+ var parse_result = vm.bundler.parse(
+ vm.bundler.allocator,
+ path,
+ loader,
+ result.dirname_fd,
+ fd,
+ hash,
+ ) orelse {
+ return error.ParseError;
+ };
+
+ // We _must_ link because:
+ // - node_modules bundle won't be properly
+ try vm.bundler.linker.link(
+ path,
+ &parse_result,
+ .absolute_path,
+ true,
+ );
+
+ if (!source_code_printer_loaded) {
+ var writer = try js_printer.BufferWriter.init(vm.allocator);
+ source_code_printer = js_printer.BufferPrinter.init(writer);
+ source_code_printer.ctx.append_null_byte = false;
+
+ source_code_printer_loaded = true;
+ }
+
+ source_code_printer.ctx.reset();
+
+ var written = try vm.bundler.print(
+ parse_result,
+ @TypeOf(&source_code_printer),
+ &source_code_printer,
+ .esm,
+ );
+
+ if (written == 0) {
+ return error.PrintingErrorWriteFailed;
+ }
+
+ return vm.allocator.dupe(u8, source_code_printer.ctx.written) catch unreachable;
+ },
+ else => {
+ return try strings.quotedAlloc(VirtualMachine.vm.allocator, path.pretty);
+ },
+ }
+ }
+ inline fn _resolve(global: *JSGlobalObject, specifier: string, source: string) !string {
+ std.debug.assert(VirtualMachine.vm_loaded);
+ std.debug.assert(VirtualMachine.vm.global == global);
+ if (strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) {
+ return Runtime.Runtime.Imports.Name;
+ }
+
+ const result: resolver.Result = vm.bundler.resolve_results.get(specifier) orelse brk: {
+ // We don't want to write to the hash table if there's an error
+ // That's why we don't use getOrPut here
+ const res = try vm.bundler.resolver.resolve(
+ Fs.PathName.init(source).dirWithTrailingSlash(),
+ specifier,
+ .stmt,
+ );
+ try vm.bundler.resolve_results.put(res.path_pair.primary.text, res);
+ break :brk res;
+ };
+
+ return result.path_pair.primary.text;
+ }
+
+ pub fn resolve(global: *JSGlobalObject, specifier: ZigString, source: ZigString) ErrorableZigString {
+ const result = _resolve(global, specifier.slice(), source.slice()) catch |err| {
+ return ErrorableZigString.errFmt(err, "ResolveError {s} for \"{s}\"\nfrom\"{s}\"", .{
+ @errorName(err),
+ specifier.slice(),
+ source.slice(),
+ });
+ };
+
+ return ErrorableZigString.ok(ZigString.init(result));
+ }
+
+ pub fn fetch(global: *JSGlobalObject, specifier: ZigString, source: ZigString) ErrorableZigString {
+ const result = _fetch(global, specifier.slice(), source.slice()) catch |err| {
+ return ErrorableZigString.errFmt(err, "{s}: \"{s}\"", .{
+ @errorName(err),
+ specifier.slice(),
+ });
+ };
+
+ return ErrorableZigString.ok(ZigString.init(result));
+ }
+
+ pub fn loadEntryPoint(this: *VirtualMachine, entry_point: string) !void {
+ var path = this.bundler.normalizeEntryPointPath(entry_point);
+
+ var promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(path));
+
+ this.global.vm().drainMicrotasks();
+ if (promise.status(this.global.vm()) == JSPromise.Status.Rejected) {
+ var str = promise.result(this.global.vm()).toWTFString(this.global);
+ Output.prettyErrorln("<r><red>Error<r>: <b>{s}<r>", .{str.slice()});
+ }
+ }
};
pub const Object = struct {
@@ -216,13 +366,14 @@ pub const EventListenerMixin = struct {
) type {
const Handler = struct {
pub fn addListener(
- ptr: *Struct,
ctx: js.JSContextRef,
function: js.JSObjectRef,
thisObject: js.JSObjectRef,
- arguments: []const js.JSValueRef,
+ argumentCount: usize,
+ _arguments: [*c]const js.JSValueRef,
exception: js.ExceptionRef,
- ) js.JSValueRef {
+ ) callconv(.C) js.JSValueRef {
+ const arguments = _arguments[0 .. argumentCount - 1];
if (arguments.len == 0 or arguments.len == 1 or !js.JSValueIsString(ctx, arguments[0]) or !js.JSValueIsObject(ctx, arguments[arguments.len - 1]) or !js.JSObjectIsFunction(ctx, arguments[arguments.len - 1])) {
return js.JSValueMakeUndefined(ctx);
}
@@ -235,10 +386,10 @@ pub const EventListenerMixin = struct {
const name_used_len = js.JSStringGetUTF8CString(arguments[0], &event_listener_names_buf, event_listener_names_buf.len);
const name = event_listener_names_buf[0 .. name_used_len - 1];
const event = EventType.match(name) orelse return js.JSValueMakeUndefined(ctx);
- var entry = VirtualMachine.instance.event_listeners.getOrPut(event) catch unreachable;
+ var entry = VirtualMachine.vm.event_listeners.getOrPut(event) catch unreachable;
if (!entry.found_existing) {
- entry.value_ptr.* = List.initCapacity(VirtualMachine.instance.allocator, 1) catch unreachable;
+ entry.value_ptr.* = List.initCapacity(VirtualMachine.vm.allocator, 1) catch unreachable;
}
var callback = arguments[arguments.len - 1];
@@ -249,6 +400,19 @@ pub const EventListenerMixin = struct {
}
};
- return Handler;
+ return NewClass(
+ Struct,
+ .{
+ .name = "addEventListener",
+ .read_only = true,
+ },
+ .{
+ .@"callAsFunction" = .{
+ .rfn = Handler.addListener,
+ .ts = d.ts{},
+ },
+ },
+ .{},
+ );
}
};
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig
index be442728b..5ee6eab94 100644
--- a/src/js_parser/js_parser.zig
+++ b/src/js_parser/js_parser.zig
@@ -1611,9 +1611,6 @@ pub const Parser = struct {
filepath_hash_for_hmr: u32 = 0,
features: RuntimeFeatures = RuntimeFeatures{},
- // When platform is set to "speedy"
- force_commonjs: bool = false,
-
// Used when bundling node_modules
enable_bundling: bool = false,
transform_require_to_import: bool = true,
@@ -1832,28 +1829,26 @@ pub const Parser = struct {
declared_symbols_i += 1;
declared_symbols[declared_symbols_i] = .{ .ref = automatic_namespace_ref, .is_top_level = true };
declared_symbols_i += 1;
- if (!p.options.force_commonjs) {
- var require_call = p.callRequireOrBundledRequire(require_call_args);
+ var require_call = p.callRequireOrBundledRequire(require_call_args);
- decls[decl_i] = G.Decl{
- .binding = p.b(
- B.Identifier{
- .ref = p.jsx_runtime_ref,
- },
- loc,
- ),
- .value = p.e(
- E.Dot{
- .target = require_call,
- .name = p.options.jsx.jsx,
- .name_loc = loc,
- .can_be_removed_if_unused = true,
- },
- loc,
- ),
- };
- decl_i += 1;
- }
+ decls[decl_i] = G.Decl{
+ .binding = p.b(
+ B.Identifier{
+ .ref = p.jsx_runtime_ref,
+ },
+ loc,
+ ),
+ .value = p.e(
+ E.Dot{
+ .target = require_call,
+ .name = p.options.jsx.jsx,
+ .name_loc = loc,
+ .can_be_removed_if_unused = true,
+ },
+ loc,
+ ),
+ };
+ decl_i += 1;
if (jsx_filename_symbol.use_count_estimate > 0) {
declared_symbols[declared_symbols_i] = .{ .ref = p.jsx_filename_ref, .is_top_level = true };
@@ -1874,34 +1869,12 @@ pub const Parser = struct {
// When everything is CommonJS
// We import JSX like this:
// var {jsxDev} = require("react/jsx-dev")
- if (p.options.force_commonjs) {
- var clause_items = p.allocator.alloc(js_ast.ClauseItem, 1) catch unreachable;
- clause_items[0] = js_ast.ClauseItem{
- .alias = p.options.jsx.jsx,
- .alias_loc = loc,
- .name = LocRef{ .loc = loc, .ref = p.jsx_runtime_ref },
- .original_name = "",
- };
- jsx_part_stmts[stmt_i] = p.s(
- S.Import{
- .namespace_ref = automatic_namespace_ref,
- .items = clause_items,
- .star_name_loc = loc,
- .is_single_line = true,
- .import_record_index = import_record_id,
- },
- loc,
- );
- // Otherwise, it looks like this
- // var
- } else {
- jsx_part_stmts[stmt_i] = p.s(S.Import{
- .namespace_ref = automatic_namespace_ref,
- .star_name_loc = loc,
- .is_single_line = true,
- .import_record_index = import_record_id,
- }, loc);
- }
+ jsx_part_stmts[stmt_i] = p.s(S.Import{
+ .namespace_ref = automatic_namespace_ref,
+ .star_name_loc = loc,
+ .is_single_line = true,
+ .import_record_index = import_record_id,
+ }, loc);
stmt_i += 1;
p.named_imports.put(
@@ -2026,7 +1999,7 @@ pub const Parser = struct {
var runtime_imports_iter = p.runtime_imports.iter();
// don't import runtime if we're bundling, it's already included
- if (!p.options.enable_bundling) {
+ if (!p.options.enable_bundling and p.has_called_runtime) {
while (runtime_imports_iter.next()) |entry| {
const imports = [_]u16{entry.key};
p.generateImportStmt(
@@ -2354,6 +2327,8 @@ pub fn NewParser(
hmr_module_class_ref: js_ast.Ref = js_ast.Ref.None,
hmr_exports_list: std.ArrayList(js_ast.ClauseItem),
+ has_called_runtime: bool = false,
+
cjs_import_stmts: std.ArrayList(Stmt),
bundle_export_ref: ?Ref = null,
@@ -2849,9 +2824,7 @@ pub fn NewParser(
// This means we need to transform from require(react) to react()
// unless we're building inside of speedy, then it's just normal commonjs
pub fn callRequireOrBundledRequire(p: *P, require_args: []Expr) Expr {
- if (p.options.force_commonjs) {
- return require_args[0];
- } else if (p.options.can_import_from_bundle) {
+ 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);
@@ -3169,15 +3142,9 @@ pub fn NewParser(
p.hoistSymbols(p.module_scope);
- if (p.options.force_commonjs) {
- p.exports_ref = try p.declareCommonJSSymbol(.unbound, "exports");
- p.module_ref = try p.declareCommonJSSymbol(.unbound, "module");
- p.require_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "require");
- } else {
- p.exports_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "exports");
- p.module_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "module");
- p.require_ref = try p.declareCommonJSSymbol(.unbound, "require");
- }
+ p.exports_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "exports");
+ p.module_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "module");
+ p.require_ref = try p.declareCommonJSSymbol(.unbound, "require");
if (p.options.enable_bundling) {
p.bundle_export_ref = try p.declareSymbol(.unbound, logger.Loc.Empty, "IF_YOU_SEE_THIS_ITS_A_BUNDLER_BUG_PLEASE_FILE_AN_ISSUE_THX");
@@ -13066,6 +13033,7 @@ pub fn NewParser(
pub fn callRuntime(p: *P, loc: logger.Loc, comptime name: string, args: []Expr) Expr {
var ref: Ref = undefined;
+ p.has_called_runtime = true;
if (!p.runtime_imports.contains(name)) {
ref = p.newSymbol(.other, name) catch unreachable;
diff --git a/src/js_printer.zig b/src/js_printer.zig
index 1ea436589..2f02e4208 100644
--- a/src/js_printer.zig
+++ b/src/js_printer.zig
@@ -3813,7 +3813,10 @@ pub fn NewFileWriter(file: std.fs.File) FileWriter {
return FileWriter.init(internal);
}
-pub const Format = enum { esm, cjs, speedy };
+pub const Format = enum {
+ esm,
+ cjs,
+};
pub fn printAst(
comptime Writer: type,
@@ -3888,38 +3891,3 @@ pub fn printCommonJS(
return @intCast(usize, std.math.max(printer.writer.written, 0));
}
-
-pub fn printSpeedyCJS(
- comptime Writer: type,
- _writer: Writer,
- tree: Ast,
- symbols: js_ast.Symbol.Map,
- source: *const logger.Source,
- ascii_only: bool,
- opts: Options,
- comptime LinkerType: type,
- linker: ?*LinkerType,
-) !usize {
- const PrinterType = NewPrinter(false, Writer, LinkerType, true, true);
- var writer = _writer;
- var printer = try PrinterType.init(
- writer,
- &tree,
- source,
- symbols,
- opts,
- linker,
- );
- for (tree.parts) |part| {
- for (part.stmts) |stmt| {
- try printer.printStmt(stmt);
- if (printer.writer.getError()) {} else |err| {
- return err;
- }
- }
- }
-
- try printer.writer.done();
-
- return @intCast(usize, std.math.max(printer.writer.written, 0));
-}
diff --git a/src/linker.zig b/src/linker.zig
index 44fd140c2..d0f48e8a1 100644
--- a/src/linker.zig
+++ b/src/linker.zig
@@ -175,6 +175,7 @@ pub fn NewLinker(comptime BundlerType: type) type {
file_path: Fs.Path,
result: *_bundler.ParseResult,
comptime import_path_format: Options.BundleOptions.ImportPathFormat,
+ comptime ignore_runtime: bool,
) !void {
var needs_runtime = result.ast.uses_exports_ref or result.ast.uses_module_ref or result.ast.runtime_imports.hasAny();
const source_dir = file_path.name.dir;
@@ -186,22 +187,24 @@ pub fn NewLinker(comptime BundlerType: type) type {
.jsx, .js, .ts, .tsx => {
for (result.ast.import_records) |*import_record, _record_index| {
const record_index = @truncate(u32, _record_index);
- if (strings.eqlComptime(import_record.path.text, Runtime.Imports.Name)) {
- // runtime is included in the bundle, so we don't need to dynamically import it
- if (linker.options.node_modules_bundle) |node_modules_bundle| {
- import_record.path.text = if (linker.options.node_modules_bundle_url.len > 0) linker.options.node_modules_bundle_url else node_modules_bundle.bundle.import_from_name;
- } else {
- import_record.path = try linker.generateImportPath(
- source_dir,
- linker.runtime_source_path,
- Runtime.version(),
- false,
- import_path_format,
- );
- result.ast.runtime_import_record_id = record_index;
- result.ast.needs_runtime = true;
+ if (comptime !ignore_runtime) {
+ if (strings.eqlComptime(import_record.path.text, Runtime.Imports.Name)) {
+ // runtime is included in the bundle, so we don't need to dynamically import it
+ if (linker.options.node_modules_bundle) |node_modules_bundle| {
+ import_record.path.text = if (linker.options.node_modules_bundle_url.len > 0) linker.options.node_modules_bundle_url else node_modules_bundle.bundle.import_from_name;
+ } else {
+ import_record.path = try linker.generateImportPath(
+ source_dir,
+ linker.runtime_source_path,
+ Runtime.version(),
+ false,
+ import_path_format,
+ );
+ result.ast.runtime_import_record_id = record_index;
+ result.ast.needs_runtime = true;
+ }
+ continue;
}
- continue;
}
if (linker.resolver.resolve(source_dir, import_record.path.text, import_record.kind)) |*_resolved_import| {
@@ -307,7 +310,7 @@ pub fn NewLinker(comptime BundlerType: type) type {
switch (err) {
error.ModuleNotFound => {
if (Resolver.isPackagePath(import_record.path.text)) {
- if (linker.options.platform != .node and Options.ExternalModules.isNodeBuiltin(import_record.path.text)) {
+ if (linker.options.platform.isWebLike() and Options.ExternalModules.isNodeBuiltin(import_record.path.text)) {
try linker.log.addRangeErrorFmt(
&result.source,
import_record.range,
diff --git a/src/main_javascript.zig b/src/main_javascript.zig
index 721098e86..be822c793 100644
--- a/src/main_javascript.zig
+++ b/src/main_javascript.zig
@@ -386,23 +386,10 @@ pub const Cli = struct {
// .entry_point,
// );
- var exception = js.JSValue.jsUndefined();
- var result = js.JSModuleLoader.evaluate(
- vm.global,
- StringS.src,
- StringS.src.len,
- "/hi.js",
- "/hi.js".len,
- js.JSValue.jsUndefined(),
- @ptrCast([*]js.JSValue, &exception),
- );
-
- if (!exception.isUndefined()) {
- var str = exception.toWTFString(vm.global);
- var slice = str.slice();
- _ = Output.errorWriter().write(slice) catch 0;
- }
+ try vm.loadEntryPoint(vm.bundler.options.entry_points[0]);
}
};
pub const JavaScript = struct {};
+
+pub const JavaScriptVirtualMachine = VirtualMachine;
diff --git a/src/options.zig b/src/options.zig
index 39b3ec0d9..c90f671a9 100644
--- a/src/options.zig
+++ b/src/options.zig
@@ -250,9 +250,9 @@ pub const Platform = enum {
speedy,
node,
- pub fn implementsRequire(platform: Platform) bool {
+ pub fn isWebLike(platform: Platform) bool {
return switch (platform) {
- .speedy, .node => true,
+ .neutral, .browser => true,
else => false,
};
}
diff --git a/src/runtime.zig b/src/runtime.zig
index 39518e58c..ac2a2f0ff 100644
--- a/src/runtime.zig
+++ b/src/runtime.zig
@@ -6,7 +6,7 @@ pub const ProdSourceContent = @embedFile("./runtime.out.js");
pub const Runtime = struct {
pub fn sourceContent() string {
- if (isDebug) {
+ if (comptime isDebug) {
var runtime_path = std.fs.path.join(std.heap.c_allocator, &[_]string{ std.fs.path.dirname(@src().file).?, "runtime.out.js" }) catch unreachable;
const file = std.fs.openFileAbsolute(runtime_path, .{}) catch unreachable;
defer file.close();
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index b65b142bf..d5f41c82e 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -80,6 +80,30 @@ pub fn endsWithAny(self: string, str: string) bool {
pub fn lastNonwhitespace(self: string, str: string) bool {}
+pub fn quotedAlloc(allocator: *std.mem.Allocator, self: string) !string {
+ var count: usize = 0;
+ for (self) |char| {
+ count += @boolToInt(char == '"');
+ }
+
+ if (count == 0) {
+ return allocator.dupe(u8, self);
+ }
+
+ var i: usize = 0;
+ var out = try allocator.alloc(u8, self.len + count);
+ for (self) |char| {
+ if (char == '"') {
+ out[i] = '\\';
+ i += 1;
+ }
+ out[i] = char;
+ i += 1;
+ }
+
+ return out;
+}
+
pub fn endsWithAnyComptime(self: string, comptime str: string) bool {
if (str.len < 10) {
const last = self[self.len - 1];
diff --git a/src/test/fixtures/console.log.js b/src/test/fixtures/console.log.js
index 3088f90d5..5a6a50b33 100644
--- a/src/test/fixtures/console.log.js
+++ b/src/test/fixtures/console.log.js
@@ -1 +1,2 @@
-console.log("Waiting for debugger.");
+import hello from "./hello";
+console.log(hello);
diff --git a/src/test/fixtures/hello.js b/src/test/fixtures/hello.js
new file mode 100644
index 000000000..34b58e6e1
--- /dev/null
+++ b/src/test/fixtures/hello.js
@@ -0,0 +1 @@
+export default "hello";