Skip to content

Commit 2bc1ac2

Browse files
committed
feat: add require.cache support
1 parent db437e8 commit 2bc1ac2

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
@@ -63,6 +63,142 @@
6363
return success;
6464
}
6565

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

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

0 commit comments

Comments
 (0)