JSDOM не загружает JavaScript, включенный в тег ‹script›

Примечание. Этот вопрос не дублирует другие существующие вопросы, потому что в этом вопросе не используется вызов функции jsdom.env(), который используется в более ранней версии JSDOM.

Файл bar.js:

console.log('bar says: hello')

Файл foo.js:

var jsdom = require('jsdom')
var html = '<!DOCTYPE html><head><script src="bar.js"></script></head><body><div>Foo</div></body>'
var window = new jsdom.JSDOM(html).window
window.onload = function () {
  console.log('window loaded')
}

Когда я запускаю foo.js, я получаю этот вывод.

$ node foo.js
window loaded

Почему не пришел bar says: hello вывод? Похоже, bar.js не был загружен. Как заставить jsdom загрузить файл в теге script?

[EDIT/SOLUTION]: проблема решена после выполнения предложения в ответе Квентина. Этот код работает:

var jsdom = require('jsdom')
var html = '<!DOCTYPE html><head><script src="bar.js"></script></head><body><div>Foo</div></body>'
var window = new jsdom.JSDOM(html, { runScripts: "dangerously", resources: "usable" }).window
window.onload = function () {
  console.log('window loaded')
}

person Lone Learner    schedule 30.10.2018    source источник
comment
@rlemon Я не понимаю, как это дубликат этого вопроса. В этом вопросе используется jsdom.env(). Это не так. Как этот ответ применим здесь?   -  person Lone Learner    schedule 30.10.2018
comment
@Lone_learner: я установил jdsom [email protected], скопировал ваш код в соответствующие bar.js и foo.js, но не могу воспроизвести ваш сценарий. Он возвращает только window loaded, но не bar says: hello. В приведенном выше коде отсутствуют некоторые настройки или код?   -  person Hugolpz    schedule 01.09.2020
comment
Я нашел еще один поток, который указывает на загрузку сервером файлов .js. github.com/jsdom/jsdom/issues/1914 . Мне все еще любопытно узнать, нашли ли Lone_Learner и @Quentin более простой способ.   -  person Hugolpz    schedule 01.09.2020


Ответы (3)


Перейдите на домашнюю страницу JSDOM.

Просматривайте заголовки, пока не найдете один с пометкой Выполнение скриптов.

Чтобы включить выполнение скриптов внутри страницы, вы можете использовать опцию runScripts: "dangerously":

const dom = new JSDOM(`<body>
  <script>document.body.appendChild(document.createElement("hr"));</script>
</body>`, { runScripts: "dangerously" });

// The script will be executed and modify the DOM:
dom.window.document.body.children.length === 2;

Опять же, мы подчеркиваем, что использовать это нужно только при отправке кода jsdom, который, как вы знаете, безопасен. Если вы используете его для произвольного пользовательского кода или кода из Интернета, вы фактически запускаете ненадежный код Node.js, и ваша машина может быть скомпрометирована.

Если вы хотите выполнять внешние скрипты, включенные через <script src="">, вам также необходимо убедиться, что они их загружают. Для этого добавьте параметр resources: "usable", как описано ниже.

person Community    schedule 30.10.2018

Учитывая, что мне не удалось воспроизвести решение на основе url из приведенного выше кода...

Брутальная альтернатива комплектам: встраивайте все!

Прочитайте различные файлы .js, вставьте их в виде строки на html-страницу. Затем дождитесь загрузки страницы, как в обычном навигаторе. Эти библиотеки загружаются в _window = new JSDOM(html, { options }).window; и поэтому доступны для вашего сценария узла.

Это, вероятно, помешает вам выполнять вызовы xhr и, следовательно, только частично решит проблему.

поздоровайся.js

// fired when loaded
console.log("say-hello.js says: hello!")
// defined and needing a call
var sayBye = function(name) { 
    var name = name ||'Hero!'; 
    console.log("say-hello.js says: Good bye! "+name)
}

основной.js:

    const fs    = require("fs");
    const jsdom = require("jsdom");
    const { JSDOM } = jsdom;
    
    var NAME  = process.env.NAME; // variable from terminal

    var html = '<!DOCTYPE html><head></head><body><div>Foo</div></body>'
    var _window = new JSDOM(html, {
        runScripts: "dangerously",
        resources: "usable" }).window;
    /* ************************************************************************* */
    /* Add scripts to head ***************************************************** */
    var jsFiles = [
        'say-hello.js'
    ];
    var scriptsContent = ``;
    for(var i =0; i< jsFiles.length;i++){
        console.log(__dirname + '/'+ jsFiles[i])
      let scriptContent = fs.readFileSync( jsFiles[i], 'utf8');
      scriptsContent = scriptsContent + `
      /* ******************************************************************************************* */
      /* `+jsFiles[i]+` **************************************************************************** */
      `+scriptContent;
    };
    let scriptElement = _window.document.createElement('script');
    scriptElement.textContent = scriptsContent;
    _window.document.head.appendChild(scriptElement);


/* ************************************************************************* */
/* Run page **************************************************************** */
_window.document.addEventListener('DOMContentLoaded', () => {
    console.log('main says: DOMContentLoaded')
    // We need to delay one extra turn because we are the first DOMContentLoaded listener,
    // but we want to execute this code only after the second DOMContentLoaded listener
    // (added by external.js) fires.
    _window.sayBye(NAME); // prints "say-hello.js says: Good bye!"

  });

Запустить его:

NAME=John node main.js    # expects hello and good bye to john messages

Источник:

person Hugolpz    schedule 01.09.2020

Согласно источнику, использование параметра JSDOM url : file://${__dirname}/index.html может сработать. Если вы протестируете его, пожалуйста, сообщите результат здесь.

person Hugolpz    schedule 28.07.2021