2
2
#include " ModuleInternalCallbacks.h"
3
3
#include < sys/stat.h>
4
4
#include < v8.h>
5
+ #include < queue>
5
6
#include < string>
6
7
#include < unordered_map>
7
8
#include " Helpers.h" // for tns::Exists
67
68
// Relative import (./ or ../)
68
69
std::string cleanSpec = spec.rfind (" ./" , 0 ) == 0 ? spec.substr (2 ) : spec;
69
70
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);
70
84
} else if (!spec.empty () && spec[0 ] == ' ~' ) {
71
- // App root alias "~/" → <ApplicationPath>/
85
+ // Alias to application root using ~/path
72
86
std::string tail = spec.size () >= 2 && spec[1 ] == ' /' ? spec.substr (2 ) : spec.substr (1 );
73
87
std::string base = RuntimeConfig.ApplicationPath + " /" + tail;
74
88
candidateBases.push_back (base);
75
89
} else if (!spec.empty () && spec[0 ] == ' /' ) {
76
90
// Absolute path within the bundle
77
91
candidateBases.push_back (spec);
78
92
} 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);
86
96
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
+ }
90
106
}
91
107
92
108
// We'll iterate these bases and attempt to resolve to an actual file
221
237
}
222
238
return v8::MaybeLocal<v8::Module>(it2->second .Get (isolate));
223
239
}
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
+ }
224
299
}
0 commit comments