Skip to content

Commit b81746d

Browse files
committed
feat: workers
1 parent 109775b commit b81746d

File tree

3 files changed

+433
-248
lines changed

3 files changed

+433
-248
lines changed

NativeScript/runtime/ModuleInternal.mm

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <Foundation/Foundation.h>
33
#include <sys/stat.h>
44
#include <time.h>
5+
#include <unistd.h>
56
#include <utime.h>
67
#include <string>
78
#include "Caches.h"
@@ -71,6 +72,9 @@ bool IsLikelyOptionalModule(const std::string& moduleName) {
7172
}
7273

7374
bool ModuleInternal::RunModule(Isolate* isolate, std::string path) {
75+
// Debug: Log module loading
76+
printf("ModuleInternal::RunModule: Loading module: %s\n", path.c_str());
77+
7478
std::shared_ptr<Caches> cache = Caches::Get(isolate);
7579
Local<Context> context = cache->GetContext();
7680
Local<Object> globalObject = context->Global();
@@ -309,12 +313,45 @@ bool IsLikelyOptionalModule(const std::string& moduleName) {
309313

310314
bool isESM = modulePath.size() >= 4 && modulePath.compare(modulePath.size() - 4, 4, ".mjs") == 0;
311315

316+
// Debug: Log ES module detection
317+
printf("LoadModule: Module path: %s, isESM: %s\n", modulePath.c_str(), isESM ? "true" : "false");
318+
312319
if (isESM) {
313320
// For ES modules the returned value is the module namespace object, not a
314321
// factory function. Wire it as the exports and skip CommonJS invocation.
322+
printf("LoadModule: Processing as ES module\n");
315323
if (!scriptValue->IsObject()) {
324+
printf("LoadModule: ES module failed - scriptValue is not an object\n");
316325
throw NativeScriptException(isolate, "Failed to load ES module " + modulePath);
317326
}
327+
printf("LoadModule: ES module loaded successfully\n");
328+
329+
// Debug: Check if we're in a worker context and if self.onmessage is set
330+
std::shared_ptr<Caches> cache = Caches::Get(isolate);
331+
if (cache->isWorker) {
332+
printf("LoadModule: In worker context, checking self.onmessage\n");
333+
Local<Context> context = isolate->GetCurrentContext();
334+
Local<Object> global = context->Global();
335+
336+
// Check if self exists
337+
Local<Value> selfValue;
338+
if (global->Get(context, ToV8String(isolate, "self")).ToLocal(&selfValue)) {
339+
printf("LoadModule: self exists: %s\n", selfValue->IsObject() ? "object" : "not object");
340+
if (selfValue->IsObject()) {
341+
Local<Object> selfObj = selfValue.As<Object>();
342+
Local<Value> onmessageValue;
343+
if (selfObj->Get(context, ToV8String(isolate, "onmessage")).ToLocal(&onmessageValue)) {
344+
printf("LoadModule: self.onmessage exists: %s\n",
345+
onmessageValue->IsFunction() ? "function" : "not function");
346+
} else {
347+
printf("LoadModule: self.onmessage does not exist\n");
348+
}
349+
}
350+
} else {
351+
printf("LoadModule: self does not exist\n");
352+
}
353+
}
354+
318355
exportsObj = scriptValue.As<Object>();
319356
bool succ =
320357
moduleObj->Set(context, tns::ToV8String(isolate, "exports"), exportsObj).FromMaybe(false);
@@ -513,9 +550,41 @@ ScriptOrigin origin(
513550
Local<Value> result;
514551
{
515552
TryCatch tcEval(isolate);
553+
printf("LoadESModule: About to evaluate module: %s\n", path.c_str());
516554
if (!module->Evaluate(context).ToLocal(&result)) {
555+
printf("LoadESModule: Evaluation failed for module: %s\n", path.c_str());
517556
throw NativeScriptException(isolate, tcEval, "Cannot evaluate module " + path);
518557
}
558+
printf("LoadESModule: Evaluation completed successfully for module: %s\n", path.c_str());
559+
560+
// Handle the case where evaluation returns a Promise (for top-level await)
561+
if (result->IsPromise()) {
562+
printf("LoadESModule: Module evaluation returned a Promise, waiting for resolution\n");
563+
Local<Promise> promise = result.As<Promise>();
564+
565+
// For worker context, we need to wait for the promise to resolve
566+
// This is important for modules that use top-level await or have async initialization
567+
std::shared_ptr<Caches> cache = Caches::Get(isolate);
568+
if (cache->isWorker) {
569+
printf("LoadESModule: In worker context, processing promise resolution\n");
570+
571+
// Run the microtask queue to allow the promise to resolve
572+
while (promise->State() == Promise::kPending) {
573+
isolate->PerformMicrotaskCheckpoint();
574+
// Add a small delay to prevent busy waiting
575+
usleep(1000); // 1ms
576+
}
577+
578+
if (promise->State() == Promise::kRejected) {
579+
printf("LoadESModule: Promise was rejected\n");
580+
Local<Value> reason = promise->Result();
581+
isolate->ThrowException(reason);
582+
throw NativeScriptException(isolate, tcEval, "Module evaluation promise rejected");
583+
}
584+
585+
printf("LoadESModule: Promise resolved successfully\n");
586+
}
587+
}
519588
}
520589

521590
// 7) Return the namespace
@@ -554,6 +623,22 @@ ScriptOrigin origin(
554623
if (path.size() >= 4 && path.compare(path.size() - 4, 4, ".mjs") == 0) {
555624
// Read raw text without wrapping.
556625
std::string sourceText = tns::ReadText(path);
626+
627+
// For ES modules in worker context, we need to provide access to global objects
628+
// since ES modules run in their own scope
629+
std::shared_ptr<Caches> cache = Caches::Get(isolate);
630+
if (cache && cache->isWorker) {
631+
// Prepend global declarations to make worker globals available in ES module scope
632+
std::string globalDeclarations = "const self = globalThis.self || globalThis;\n"
633+
"const postMessage = globalThis.postMessage;\n"
634+
"const close = globalThis.close;\n"
635+
"const importScripts = globalThis.importScripts;\n"
636+
"const console = globalThis.console;\n"
637+
"\n";
638+
639+
sourceText = globalDeclarations + sourceText;
640+
}
641+
557642
return tns::ToV8String(isolate, sourceText);
558643
}
559644

NativeScript/runtime/ModuleInternalCallbacks.mm

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ static bool IsNodeBuiltinModule(const std::string& moduleName) {
6565
return v8::MaybeLocal<v8::Module>();
6666
}
6767

68+
// Debug: Log all module resolution attempts, especially for @nativescript/core/globals
69+
std::shared_ptr<Caches> cache = Caches::Get(isolate);
70+
if (cache->isWorker) {
71+
printf("ResolveModuleCallback: Worker trying to resolve '%s'\n", spec.c_str());
72+
}
73+
6874
// 2) Find which filepath the referrer was compiled under
6975
std::string referrerPath;
7076
for (auto& kv : g_moduleRegistry) {
@@ -129,6 +135,12 @@ static bool IsNodeBuiltinModule(const std::string& moduleName) {
129135
std::string tail = spec.size() >= 2 && spec[1] == '/' ? spec.substr(2) : spec.substr(1);
130136
std::string base = RuntimeConfig.ApplicationPath + "/" + tail;
131137
candidateBases.push_back(base);
138+
139+
// Debug: Log tilde resolution for worker context
140+
if (cache->isWorker) {
141+
printf("ResolveModuleCallback: Worker resolving tilde path '%s' -> '%s'\n", spec.c_str(),
142+
base.c_str());
143+
}
132144
} else if (!spec.empty() && spec[0] == '/') {
133145
// Absolute path within the bundle
134146
candidateBases.push_back(spec);
@@ -213,9 +225,15 @@ static bool IsNodeBuiltinModule(const std::string& moduleName) {
213225

214226
// At this point, absPath is either a valid file or last attempted candidate.
215227

216-
// If we still didnt resolve to an actual file, surface an exception instead
228+
// If we still didn't resolve to an actual file, surface an exception instead
217229
// of letting ReadModule() assert while trying to open a directory.
218230
if (!isFile(absPath)) {
231+
// Debug: Log resolution failure for worker context
232+
if (cache->isWorker) {
233+
printf("ResolveModuleCallback: Worker failed to resolve '%s' -> '%s'\n", spec.c_str(),
234+
absPath.c_str());
235+
}
236+
219237
// Check if this is a Node.js built-in module (e.g., node:url)
220238
if (IsNodeBuiltinModule(spec)) {
221239
// Strip the "node:" prefix and try to resolve as a regular module
@@ -326,9 +344,20 @@ static bool IsNodeBuiltinModule(const std::string& moduleName) {
326344

327345
// Special handling for JSON imports (e.g. import data from './foo.json' assert {type:'json'})
328346
if (absPath.size() >= 5 && absPath.compare(absPath.size() - 5, 5, ".json") == 0) {
347+
// Debug: Log JSON module handling for worker context
348+
if (cache->isWorker) {
349+
printf("ResolveModuleCallback: Worker handling JSON module '%s'\n", absPath.c_str());
350+
}
351+
329352
// Read file contents
330353
std::string jsonText = tns::ReadText(absPath);
331354

355+
// Debug: Log JSON content preview for worker context
356+
if (cache->isWorker) {
357+
std::string preview = jsonText.length() > 200 ? jsonText.substr(0, 200) + "..." : jsonText;
358+
printf("ResolveModuleCallback: Worker JSON content preview: %s\n", preview.c_str());
359+
}
360+
332361
// Build a small ES module that just exports the parsed JSON as default
333362
std::string moduleSource = "export default " + jsonText + ";";
334363

@@ -367,16 +396,28 @@ static bool IsNodeBuiltinModule(const std::string& moduleName) {
367396
return v8::MaybeLocal<v8::Module>(jsonModule);
368397
}
369398

370-
// 5) If weve already compiled that module (non-JSON case), return it
399+
// 5) If we've already compiled that module (non-JSON case), return it
371400
auto it = g_moduleRegistry.find(absPath);
372401
if (it != g_moduleRegistry.end()) {
402+
if (cache->isWorker) {
403+
printf("ResolveModuleCallback: Worker found cached module '%s' -> '%s'\n", spec.c_str(),
404+
absPath.c_str());
405+
}
373406
return v8::MaybeLocal<v8::Module>(it->second.Get(isolate));
374407
}
375408

376409
// 6) Otherwise, compile & register it
410+
if (cache->isWorker) {
411+
printf("ResolveModuleCallback: Worker compiling new module '%s' -> '%s'\n", spec.c_str(),
412+
absPath.c_str());
413+
}
377414
try {
378415
tns::ModuleInternal::LoadScript(isolate, absPath);
379416
} catch (NativeScriptException& ex) {
417+
if (cache->isWorker) {
418+
printf("ResolveModuleCallback: Worker failed to compile module '%s' -> '%s'\n", spec.c_str(),
419+
absPath.c_str());
420+
}
380421
ex.ReThrowToV8(isolate);
381422
return v8::MaybeLocal<v8::Module>();
382423
}

0 commit comments

Comments
 (0)