22#include " ModuleInternalCallbacks.h"
33#include < sys/stat.h>
44#include < v8.h>
5+ #include < queue>
56#include < string>
67#include < unordered_map>
78#include " Helpers.h" // for tns::Exists
6768 // Relative import (./ or ../)
6869 std::string cleanSpec = spec.rfind (" ./" , 0 ) == 0 ? spec.substr (2 ) : spec;
6970 candidateBases.push_back (baseDir + cleanSpec);
71+ } else if (spec.rfind (" file://" , 0 ) == 0 ) {
72+ // Absolute file URL, e.g. file:///app/path/to/chunk.mjs
73+ std::string tail = spec.substr (7 ); // strip file://
74+ if (tail.rfind (" /" , 0 ) != 0 ) {
75+ tail = " /" + tail;
76+ }
77+ // If starts with /app/... drop the leading /app
78+ const std::string appPrefix = " /app/" ;
79+ if (tail.rfind (appPrefix, 0 ) == 0 ) {
80+ tail = tail.substr (appPrefix.size ());
81+ }
82+ std::string base = RuntimeConfig.ApplicationPath + " /" + tail;
83+ candidateBases.push_back (base);
7084 } else if (!spec.empty () && spec[0 ] == ' ~' ) {
71- // App root alias "~/" → <ApplicationPath>/
85+ // Alias to application root using ~/path
7286 std::string tail = spec.size () >= 2 && spec[1 ] == ' /' ? spec.substr (2 ) : spec.substr (1 );
7387 std::string base = RuntimeConfig.ApplicationPath + " /" + tail;
7488 candidateBases.push_back (base);
7589 } else if (!spec.empty () && spec[0 ] == ' /' ) {
7690 // Absolute path within the bundle
7791 candidateBases.push_back (spec);
7892 } else {
79- // Bare specifier – look inside tns_modules like the CommonJS resolver
80- NSString * tnsModulesPath =
81- [[NSString stringWithUTF8String: RuntimeConfig.ApplicationPath.c_str ()]
82- stringByAppendingPathComponent: @" tns_modules" ];
83-
84- std::string base1 = std::string ([tnsModulesPath UTF8String ]) + " /" + spec;
85- candidateBases.push_back (base1);
93+ // Bare specifier – resolve relative to the application root directory
94+ std::string base = RuntimeConfig.ApplicationPath + " /" + spec;
95+ candidateBases.push_back (base);
8696
87- // Fallback to tns-core-modules/<spec>
88- std::string base2 = base1 + " /tns-core-modules/" + spec;
89- candidateBases.push_back (base2);
97+ // Additional heuristic: Webpack encodes path separators as underscores in
98+ // chunk IDs (e.g. "src_app_components_foo_bar_ts.mjs"). Try converting
99+ // those underscores back to slashes and look for that file as well.
100+ std::string withSlashes = spec;
101+ std::replace (withSlashes.begin (), withSlashes.end (), ' _' , ' /' );
102+ std::string baseSlashes = RuntimeConfig.ApplicationPath + " /" + withSlashes;
103+ if (baseSlashes != base) {
104+ candidateBases.push_back (baseSlashes);
105+ }
90106 }
91107
92108 // We'll iterate these bases and attempt to resolve to an actual file
221237 }
222238 return v8::MaybeLocal<v8::Module>(it2->second .Get (isolate));
223239}
240+
241+ // ────────────────────────────────────────────────────────────────────────────
242+ // Dynamic import() host callback
243+ v8::MaybeLocal<v8::Promise> ImportModuleDynamicallyCallback (
244+ v8::Local<v8::Context> context, v8::Local<v8::ScriptOrModule> referrer,
245+ v8::Local<v8::String> specifier, v8::Local<v8::FixedArray> import_assertions) {
246+ v8::Isolate* isolate = context->GetIsolate ();
247+ v8::EscapableHandleScope scope (isolate);
248+
249+ // Create a Promise resolver we'll resolve/reject synchronously for now.
250+ v8::Local<v8::Promise::Resolver> resolver = v8::Promise::Resolver::New (context).ToLocalChecked ();
251+
252+ // Re-use the static resolver to locate / compile the module.
253+ try {
254+ v8::Local<v8::Module> refMod; // empty -> ResolveModuleCallback falls back to absPath logic
255+ v8::MaybeLocal<v8::Module> maybeModule =
256+ ResolveModuleCallback (context, specifier, import_assertions, refMod);
257+
258+ v8::Local<v8::Module> module ;
259+ if (!maybeModule.ToLocal (&module )) {
260+ // resolution failed → reject
261+ std::string specStr = tns::ToString (isolate, specifier);
262+ std::string errMsg = " Cannot resolve module " + specStr;
263+ resolver->Reject (context, v8::Exception::Error (tns::ToV8String (isolate, errMsg))).Check ();
264+ return scope.Escape (resolver->GetPromise ());
265+ }
266+
267+ // If not yet instantiated/evaluated, do it now
268+ if (module ->GetStatus () == v8::Module::kUninstantiated ) {
269+ if (!module ->InstantiateModule (context, &ResolveModuleCallback).FromMaybe (false )) {
270+ resolver
271+ ->Reject (context,
272+ v8::Exception::Error (tns::ToV8String (isolate, " Failed to instantiate module" )))
273+ .Check ();
274+ return scope.Escape (resolver->GetPromise ());
275+ }
276+ }
277+
278+ if (module ->GetStatus () != v8::Module::kEvaluated ) {
279+ if (module ->Evaluate (context).IsEmpty ()) {
280+ resolver
281+ ->Reject (context,
282+ v8::Exception::Error (tns::ToV8String (isolate, " Failed to evaluate module" )))
283+ .Check ();
284+ return scope.Escape (resolver->GetPromise ());
285+ }
286+ }
287+
288+ resolver->Resolve (context, module ->GetModuleNamespace ()).Check ();
289+ } catch (NativeScriptException& ex) {
290+ ex.ReThrowToV8 (isolate);
291+ resolver
292+ ->Reject (context, v8::Exception::Error (
293+ tns::ToV8String (isolate, " Native error during dynamic import" )))
294+ .Check ();
295+ }
296+
297+ return scope.Escape (resolver->GetPromise ());
298+ }
224299}
0 commit comments