Skip to content

Commit 798f27a

Browse files
committed
feat: add require.cache support
1 parent 6ec9a8f commit 798f27a

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

NativeScript/runtime/ModuleInternal.h

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ class ModuleInternal {
2323
v8::ScriptCompiler::CachedData* LoadScriptCache(const std::string& path);
2424
void SaveScriptCache(const v8::Local<v8::Script> script, const std::string& path);
2525
std::string GetCacheFileName(const std::string& path);
26+
27+
static void cacheGetterCallback(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info);
28+
static void cacheSetterCallback(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info);
29+
static void cacheDeleterCallback(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo< v8::Boolean > &info);
30+
static void cacheEnumeratorCallback(const v8::PropertyCallbackInfo<v8::Array>& info);
2631

2732
std::unique_ptr<v8::Persistent<v8::Function>> requireFunction_;
2833
std::unique_ptr<v8::Persistent<v8::Function>> requireFactoryFunction_;

NativeScript/runtime/ModuleInternal.mm

+146
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,142 @@
6464
return success;
6565
}
6666

67+
void ModuleInternal::cacheGetterCallback(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
68+
v8::Isolate* isolate = info.GetIsolate();
69+
if(!property->IsString()) {
70+
return;
71+
}
72+
try {
73+
//ModuleInternal* moduleInternal = static_cast<ModuleInternal*>(info.Data().As<External>()->Value());
74+
ModuleInternal* moduleInternal = static_cast<ModuleInternal*>(info.Data().As<v8::Array>()->Get(isolate->GetCurrentContext(), 0).ToLocalChecked().As<External>()->Value());
75+
std::string moduleName = tns::ToString(isolate, property);
76+
auto it = moduleInternal->loadedModules_.find(moduleName);
77+
if (it != moduleInternal->loadedModules_.end()) {
78+
info.GetReturnValue().Set(it->second->Get(isolate));
79+
return;
80+
}
81+
std::string callingModuleDirName = tns::ToString(isolate, info.Data().As<v8::Array>()->Get(isolate->GetCurrentContext(), 1).ToLocalChecked()); //tns::ToString(isolate, info[1].As<v8::String>());
82+
83+
NSString* fullPath;
84+
if (moduleName.length() > 0 && moduleName[0] != '/') {
85+
if (moduleName[0] == '.') {
86+
fullPath = [[NSString stringWithUTF8String:callingModuleDirName.c_str()] stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]];
87+
} else if (moduleName[0] == '~') {
88+
moduleName = moduleName.substr(2);
89+
fullPath = [[NSString stringWithUTF8String:RuntimeConfig.ApplicationPath.c_str()] stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]];
90+
} else {
91+
NSString* tnsModulesPath = [[NSString stringWithUTF8String:RuntimeConfig.ApplicationPath.c_str()] stringByAppendingPathComponent:@"tns_modules"];
92+
fullPath = [tnsModulesPath stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]];
93+
94+
const char* path1 = [fullPath fileSystemRepresentation];
95+
const char* path2 = [[fullPath stringByAppendingPathExtension:@"js"] fileSystemRepresentation];
96+
97+
if (!tns::Exists(path1) && !tns::Exists(path2)) {
98+
fullPath = [tnsModulesPath stringByAppendingPathComponent:@"tns-core-modules"];
99+
fullPath = [fullPath stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]];
100+
}
101+
}
102+
} else {
103+
fullPath = [NSString stringWithUTF8String:moduleName.c_str()];
104+
}
105+
NSString* fileNameOnly = [fullPath lastPathComponent];
106+
NSString* pathOnly = [fullPath stringByDeletingLastPathComponent];
107+
std::string fileNameOnlyStr = [fileNameOnly UTF8String];
108+
std::string pathOnlyStr = [pathOnly UTF8String];
109+
size_t lastIndex = fileNameOnlyStr.find_last_of(".");
110+
std::string moduleNameWithoutExtension = (lastIndex == std::string::npos) ? fileNameOnlyStr : fileNameOnlyStr.substr(0, lastIndex);
111+
std::string cacheKey = pathOnlyStr + "*" + moduleNameWithoutExtension;
112+
113+
it = moduleInternal->loadedModules_.find(cacheKey);
114+
115+
if (it != moduleInternal->loadedModules_.end()) {
116+
info.GetReturnValue().Set(it->second->Get(isolate));
117+
}
118+
} catch (NativeScriptException& ex) {
119+
ex.ReThrowToV8(isolate);
120+
}
121+
122+
}
123+
void ModuleInternal::cacheSetterCallback(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) {
124+
// TODO: maybe implement?
125+
}
126+
void ModuleInternal::cacheDeleterCallback(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo< v8::Boolean > &info) {
127+
v8::Isolate* isolate = info.GetIsolate();
128+
if(!property->IsString()) {
129+
return;
130+
}
131+
try {
132+
//ModuleInternal* moduleInternal = static_cast<ModuleInternal*>(info.Data().As<External>()->Value());
133+
ModuleInternal* moduleInternal = static_cast<ModuleInternal*>(info.Data().As<v8::Array>()->Get(isolate->GetCurrentContext(), 0).ToLocalChecked().As<External>()->Value());
134+
std::string moduleName = tns::ToString(isolate, property);
135+
std::string callingModuleDirName = tns::ToString(isolate, info.Data().As<v8::Array>()->Get(isolate->GetCurrentContext(), 1).ToLocalChecked()); //tns::ToString(isolate, info[1].As<v8::String>());
136+
137+
NSString* fullPath;
138+
if (moduleName.length() > 0 && moduleName[0] != '/') {
139+
if (moduleName[0] == '.') {
140+
fullPath = [[NSString stringWithUTF8String:callingModuleDirName.c_str()] stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]];
141+
} else if (moduleName[0] == '~') {
142+
moduleName = moduleName.substr(2);
143+
fullPath = [[NSString stringWithUTF8String:RuntimeConfig.ApplicationPath.c_str()] stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]];
144+
} else {
145+
NSString* tnsModulesPath = [[NSString stringWithUTF8String:RuntimeConfig.ApplicationPath.c_str()] stringByAppendingPathComponent:@"tns_modules"];
146+
fullPath = [tnsModulesPath stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]];
147+
148+
const char* path1 = [fullPath fileSystemRepresentation];
149+
const char* path2 = [[fullPath stringByAppendingPathExtension:@"js"] fileSystemRepresentation];
150+
151+
if (!tns::Exists(path1) && !tns::Exists(path2)) {
152+
fullPath = [tnsModulesPath stringByAppendingPathComponent:@"tns-core-modules"];
153+
fullPath = [fullPath stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]];
154+
}
155+
}
156+
} else {
157+
fullPath = [NSString stringWithUTF8String:moduleName.c_str()];
158+
}
159+
NSString* fileNameOnly = [fullPath lastPathComponent];
160+
NSString* pathOnly = [fullPath stringByDeletingLastPathComponent];
161+
std::string fileNameOnlyStr = [fileNameOnly UTF8String];
162+
std::string pathOnlyStr = [pathOnly UTF8String];
163+
size_t lastIndex = fileNameOnlyStr.find_last_of(".");
164+
std::string moduleNameWithoutExtension = (lastIndex == std::string::npos) ? fileNameOnlyStr : fileNameOnlyStr.substr(0, lastIndex);
165+
std::string cacheKey = pathOnlyStr + "*" + moduleNameWithoutExtension;
166+
// auto str = moduleInternal->ResolvePath(isolate, [pathOnly UTF8String], [fileNameOnly UTF8String]);
167+
auto it = moduleInternal->loadedModules_.find(cacheKey);
168+
169+
if (it != moduleInternal->loadedModules_.end()) {
170+
moduleInternal->loadedModules_.erase(it);
171+
Local<Value> outStr;
172+
bool success = it->second->Get(isolate)->Get(isolate->GetCurrentContext(), tns::ToV8String(isolate, "id")).ToLocal(&outStr);
173+
if(success) {
174+
it = moduleInternal->loadedModules_.find(tns::ToString(isolate, outStr));
175+
if (it != moduleInternal->loadedModules_.end()) {
176+
moduleInternal->loadedModules_.erase(it);
177+
}
178+
}
179+
info.GetReturnValue().Set(v8::Boolean::New(isolate, true));
180+
}
181+
} catch (NativeScriptException& ex) {
182+
ex.ReThrowToV8(isolate);
183+
}
184+
}
185+
void ModuleInternal::cacheEnumeratorCallback(const v8::PropertyCallbackInfo<v8::Array>& info) {
186+
v8::Isolate* isolate = info.GetIsolate();
187+
try {
188+
ModuleInternal* moduleInternal = static_cast<ModuleInternal*>(info.Data().As<v8::Array>()->Get(isolate->GetCurrentContext(), 0).ToLocalChecked().As<External>()->Value());
189+
v8::Local<v8::Array> result = v8::Array::New(isolate, (int)moduleInternal->loadedModules_.size());
190+
int i = 0;
191+
for(auto it = moduleInternal->loadedModules_.begin(); it != moduleInternal->loadedModules_.end(); ++it) {
192+
bool success = result->Set(isolate->GetCurrentContext(), i++, tns::ToV8String(isolate, it->first)).FromMaybe(false);
193+
tns::Assert(success, isolate);
194+
}
195+
info.GetReturnValue().Set(result);
196+
197+
} catch (NativeScriptException& ex) {
198+
ex.ReThrowToV8(isolate);
199+
}
200+
201+
}
202+
67203
Local<v8::Function> ModuleInternal::GetRequireFunction(Isolate* isolate, const std::string& dirName) {
68204
Local<v8::Function> requireFuncFactory = requireFactoryFunction_->Get(isolate);
69205
Local<Context> context = isolate->GetCurrentContext();
@@ -76,6 +212,16 @@
76212
Local<Object> thiz = Object::New(isolate);
77213
bool success = requireFuncFactory->Call(context, thiz, 2, args).ToLocal(&result);
78214
tns::Assert(success && !result.IsEmpty() && result->IsFunction(), isolate);
215+
v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
216+
Local<v8::Array> cacheArgs = v8::Array::New(isolate, 2);
217+
success = cacheArgs->Set(context, 0, External::New(isolate, this)).FromMaybe(false);
218+
tns::Assert(success, isolate);
219+
success = cacheArgs->Set(context, 1, tns::ToV8String(isolate, dirName.c_str())).FromMaybe(false);
220+
tns::Assert(success, isolate);
221+
templ->SetHandler(v8::NamedPropertyHandlerConfiguration(cacheGetterCallback, cacheSetterCallback, nullptr, cacheDeleterCallback, cacheEnumeratorCallback, cacheArgs));
222+
223+
success = result.As<v8::Function>()->DefineOwnProperty(context, tns::ToV8String(isolate, "cache"), templ->NewInstance(context).ToLocalChecked()).FromMaybe(false);
224+
tns::Assert(success, isolate);
79225

80226
return result.As<v8::Function>();
81227
}

0 commit comments

Comments
 (0)