Как использовать несколько файлов js с Duktape? - PullRequest
1 голос
/ 25 мая 2020

Я использую Duktape во встроенном MCU. Для тестового примера у меня есть: main. js file:

(function(){
    test();
})();

test. js file:

(function test(){
    print("func");
})

Оба скомпилированы как глобальный код по умолчанию и main. js выполняется с помощью duk_call (ctx, 0);

Проблема в том, что он выдает ошибку при вызове функции test ().

Я также пробовал использовать только

function test() {
   print("test");
}

в тесте. js код, но он тоже не работает.

Насколько я понимаю, оба файла имеют отдельный контекст выполнения. Вот почему функция недоступна.

Но как правильно разделить код на несколько файлов для Duktape?

PS Я стараюсь избегать использования глобального контекста, потому что в документации сказано этот способ доступа к переменным медленный, поэтому main. js выглядит именно так.

PPS Я уверен, что функция test () недоступна, но я не знаю, как написать js код, чтобы все работало.

PPPS print () - это функция C, которая выводит на последовательный порт esp32 и работает. даже main. js работает без вызова функции test ().

1 Ответ

1 голос
/ 27 мая 2020

По сути, вам нужна функция импорта файлов. Вы можете реализовать это двумя способами:

  1. Обеспечьте функцию в вашем бэкэнде и экспортируйте ее в JS, чтобы разрешить динамическую загрузку файла во время выполнения.
  2. Реализовать обработку модуля, например в Node.js (что, по сути, также сводится к функции импорта).

Вторая идея - это то, что используется чаще всего, и реализует четко определенный подход для включения других файлов в ваше JS приложение. Duktape поставляется с дополнительным файлом, который реализует команду require, как и в Node.js. Вам нужно только предоставить свои собственные функции для разрешения модуля и загрузки его с диска (поскольку duktape не поддерживает файловый ввод-вывод).

Я реализовал этот подход в инструменте MGA в MySQL Верстак . Файл duktape для реализации обработки узловых модулей - , здесь . Функция разрешения модулей (которая включает обработку вложенных папок node_modules и т.д. c.) Реализована в классе ScriptingContext . Соответствующая его часть такова:

/**
 * Part of the module loading machinery. JS interfacing is done by the duk_module_node code.
 * But we have to do the file work here. On the stack we get the value passed to `require()` as a "module ID" and
 * the ID of the calling script (which is empty for the main script).
 */
duk_ret_t ScriptingContext::resolveModule(duk_context *ctx) {
  // stack: [ requested_id parent_id ]
  std::string requestedID = duk_get_string(ctx, 0);
  std::string callingID = duk_get_string(ctx, 1);
  std::string parentPath = FS::isDir(callingID) ? callingID : Path::dirname(callingID);

  // Module resolution strategy in Node.js style: https://nodejs.org/api/modules.html#modules_all_together
  auto modules = getInternalModules();
  if (modules.find(requestedID) != modules.end()) {
    duk_push_string(ctx, requestedID.c_str());
    return 1;
  }

  ScriptingContext *context = ScriptingContext::fromDuktapeContext(ctx);
  std::string resolvedID;
  std::string cwd = Process::cwd();

  try {
    if (Path::isAbsolute(requestedID) || Utilities::hasPrefix(requestedID, ".")) {
      std::string temp;
      if (Path::isAbsolute(requestedID)) {
        temp = Path::relative(cwd, requestedID);
      } else
        temp = Path::join({ parentPath, requestedID });

      resolvedID = resolveFile(temp);
      if (resolvedID.empty())
        resolvedID = resolveFolder(context, temp);
    }
  } catch (std::runtime_error &e) {
    // Triggered for parse errors in package.json.
    context->throwScriptingError(ScriptingError::Syntax, e.what());
    return 0;
  }

  // No files found so far. Check node modules.
  if (resolvedID.empty()) {
    for (auto &folder : moduleFolders(parentPath)) {
      std::string path = Path::join({ folder, requestedID });
      std::string temp = resolveFile(path);
      if (!temp.empty()) {
        resolvedID = temp;
        break;
      }

      temp = resolveFolder(context, path);
      if (!temp.empty()) {
        resolvedID = temp;
        break;
      }
    }
  }

  if (resolvedID.empty()) {
    context->throwScriptingError(ScriptingError::Error, Utilities::format("Cannot resolve module %s", requestedID.c_str()));
    return 0;
  }

  duk_push_string(ctx, resolvedID.c_str());
  return 1;  // Use result on stack.
}
...