1+ #include < filesystem>
12#pragma warning(disable : 4251)
23
3- #include " main/NodeJsHelper.h"
4-
54#include " api/EventAPI.h"
65#include " engine/EngineManager.h"
76#include " engine/EngineOwnData.h"
87#include " engine/RemoteCall.h"
8+ #include " fmt/format.h"
99#include " ll/api/chrono/GameChrono.h"
1010#include " ll/api/coro/CoroTask.h"
1111#include " ll/api/io/FileUtils.h"
1414#include " ll/api/thread/ServerThreadExecutor.h"
1515#include " ll/api/utils/StringUtils.h"
1616#include " main/Global.h"
17+ #include " main/NodeJsHelper.h"
1718#include " uv/uv.h"
1819#include " v8/v8.h"
1920
@@ -130,14 +131,17 @@ script::ScriptEngine* newEngine() {
130131 return engine;
131132}
132133
133- bool loadPluginCode (script::ScriptEngine* engine, std::string entryScriptPath, std::string pluginDirPath) {
134- auto mainScripts = ll::file_utils::readFile (ll::string_utils::str2u8str (entryScriptPath));
135- if (!mainScripts) {
136- return false ;
137- }
138-
134+ bool loadPluginCode (script::ScriptEngine* engine, std::string entryScriptPath, std::string pluginDirPath, bool esm) {
139135 // Process requireDir
140136 if (!pluginDirPath.ends_with (' /' )) pluginDirPath += " /" ;
137+
138+ // check if entryScriptPath is not absolute path
139+ if (auto path = std::filesystem::path (entryScriptPath); !path.is_absolute ()) {
140+ entryScriptPath = std::filesystem::absolute (path).string ();
141+ }
142+ if (auto path = std::filesystem::path (pluginDirPath); !path.is_absolute ()) {
143+ pluginDirPath = std::filesystem::absolute (path).string ();
144+ }
141145 pluginDirPath = ll::string_utils::replaceAll (pluginDirPath, " \\ " , " /" );
142146 entryScriptPath = ll::string_utils::replaceAll (entryScriptPath, " \\ " , " /" );
143147
@@ -151,23 +155,58 @@ bool loadPluginCode(script::ScriptEngine* engine, std::string entryScriptPath, s
151155 using namespace v8 ;
152156 EngineScope enter (engine);
153157
154- string executeJs = " const __LLSE_PublicRequire = "
155- " require('module').createRequire(process.cwd() + '/"
156- + pluginDirPath + " ');"
157- + " const __LLSE_PublicModule = require('module'); "
158- " __LLSE_PublicModule.exports = {};"
159- + " ll.export = ll.exports; ll.import = ll.imports; "
160-
161- + " (function (exports, require, module, __filename, __dirname) { " + mainScripts.value ()
162- + " \n })({}, __LLSE_PublicRequire, __LLSE_PublicModule, '" + entryScriptPath + " ', '"
163- + pluginDirPath + " '); " ; // TODO __filename & __dirname need to be reviewed
164- // TODO: ESM Support
158+ string compiler;
159+ if (esm) {
160+ compiler = fmt::format (
161+ R"(
162+ import('url').then(url => {{
163+ const moduleUrl = url.pathToFileURL('{1}').href;
164+ import(moduleUrl).catch(error => {{
165+ console.error('Failed to load ESM module:', error);
166+ process.exit(1);
167+ }});
168+ }}).catch(error => {{
169+ console.error('Failed to import url module:', error);
170+ process.exit(1);
171+ }});
172+ )" ,
173+ pluginDirPath,
174+ entryScriptPath
175+ );
176+ } else {
177+ compiler = fmt::format (
178+ R"(
179+ const __Path = require("path");
180+ const __PluginPath = __Path.join("{0}");
181+ const __PluginNodeModulesPath = __Path.join(__PluginPath, "node_modules");
182+
183+ __dirname = __PluginPath;
184+ __filename = "{1}";
185+ (function ReplaeRequire() {{
186+ const PublicModule = require('module').Module;
187+ const OriginalResolveLookupPaths = PublicModule._resolveLookupPaths;
188+ PublicModule._resolveLookupPaths = function (request, parent) {{
189+ let result = OriginalResolveLookupPaths.call(this, request, parent);
190+ if (Array.isArray(result)) {{
191+ result.push(__PluginNodeModulesPath);
192+ result.push(__PluginPath);
193+ }}
194+ return result;
195+ }};
196+ require = PublicModule.createRequire(__PluginPath);
197+ }})();
198+ require("{1}");
199+ )" ,
200+ pluginDirPath,
201+ entryScriptPath
202+ );
203+ }
165204
166205 // Set exit handler
167206 node::SetProcessExitHandler (env, [](node::Environment* env_, int exit_code) { stopEngine (getEngine (env_)); });
168207
169208 // Load code
170- MaybeLocal<v8::Value> loadenv_ret = node::LoadEnvironment (env, executeJs .c_str ());
209+ MaybeLocal<v8::Value> loadenv_ret = node::LoadEnvironment (env, compiler .c_str ());
171210 if (loadenv_ret.IsEmpty ()) // There has been a JS exception.
172211 {
173212 node::Stop (env);
@@ -311,6 +350,25 @@ bool doesPluginPackHasDependency(const std::string& dirPath) {
311350 }
312351}
313352
353+ bool isESModulesSystem (const std::string& dirPath) {
354+ auto dirPath_obj = std::filesystem::path (dirPath);
355+
356+ std::filesystem::path packageFilePath = dirPath_obj / std::filesystem::path (" package.json" );
357+ if (!std::filesystem::exists (packageFilePath)) return false ;
358+
359+ try {
360+ std::ifstream file (ll::string_utils::u8str2str (packageFilePath.make_preferred ().u8string ()));
361+ nlohmann::json j;
362+ file >> j;
363+ if (j.contains (" type" ) && j[" type" ] == " module" ) {
364+ return true ;
365+ }
366+ return false ;
367+ } catch (...) {
368+ return false ;
369+ }
370+ }
371+
314372bool processConsoleNpmCmd (const std::string& cmd) {
315373#ifdef LEGACY_SCRIPT_ENGINE_BACKEND_NODEJS
316374 if (cmd.starts_with (" npm " )) {
0 commit comments