capricorn86 / happy-dom

A JavaScript implementation of a web browser without its graphical user interface
MIT License
3.14k stars 189 forks source link

`document` not defined for imported libraries? #1361

Closed thonkinator closed 3 months ago

thonkinator commented 4 months ago

I'm importing the paper npm package, and I'm trying to use happy-dom because paper.js seems to rely on some browser APIs for some features. However, I'm getting errors about document being undefined, like this:

import { GlobalRegistrator } from "@happy-dom/global-registrator";

GlobalRegistrator.register();

import paper from "paper";

paper.setup([36, 36]);
const circle = new paper.Shape.Circle([18, 18], 18);

paper.project.activeLayer.importSVG(
    `... svg data here ...`
);

Gives this error:

15632 |                 function onError(message, status) {
15633 |                         var onError = options.onError;
15634 |                         if (onError) {
15635 |                                 onError(message, status);
15636 |                         } else {
15637 |                                 throw new Error(message);
                  ^
error: TypeError: undefined is not an object (evaluating 'document.body')
      at onError (node_modules/paper/dist/paper-full.js:15637:11)
      at onLoad (node_modules/paper/dist/paper-full.js:15628:5)
      at importSVG (node_modules/paper/dist/paper-full.js:15663:4)
      at importSVG (node_modules/paper/dist/paper-full.js:15671:11)
      at index.ts:11:1

Is document being undefined a bug with happy-dom, or is it just a limitation of the global registrator? Either way, what can I do to get this working?

odanado commented 3 months ago

It looks like the issue you're encountering might be related to the way paper is being imported and initialized alongside @happy-dom/global-registrator. The error indicates that paper.js is trying to access the document object before it's fully registered by happy-dom. To address this, you could try dynamically importing paper after happy-dom has registered the global document. Here's how you could adjust your code:

import { GlobalRegistrator } from "@happy-dom/global-registrator";

GlobalRegistrator.register();

const paper = await import("paper");

paper.setup([36, 36]);
const circle = new paper.Shape.Circle([18, 18], 18);

paper.project.activeLayer.importSVG(`... svg data here ...`);
thonkinator commented 3 months ago

that does seem to make document defined for the library, but the load fails anyway because apparently the library relies on getting a canvas context, which is unsupported by happy-dom. however, paper.js supports working without a canvas (like in a worker context) which it does by checking for window, so manually defining window as null makes it work. thank you!

import { GlobalRegistrator } from "@happy-dom/global-registrator";

GlobalRegistrator.register();

Object.define(global, "window", {
    value: undefined,
});

const paper = await import("paper");

paper.setup([36, 36]);
const circle = new paper.Shape.Circle([18, 18], 18);

paper.project.activeLayer.importSVG(`... svg data here ...`);
capricorn86 commented 3 months ago

Thank you for reporting @thonkinator and thank you for helping out @odanado! :slightly_smiling_face: