Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/454 implement vm inside happy dom #458

Merged
merged 8 commits into from
Apr 26, 2022
256 changes: 128 additions & 128 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/global-registrator/src/GlobalRegistrator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Window } from 'happy-dom';
import { GlobalWindow } from 'happy-dom';

/**
*
Expand All @@ -13,7 +13,7 @@ export default class GlobalRegistrator {
if (this.registered.length) {
throw new Error('Failed to registered. Happy DOM has already been globally registered.');
}
const window = new Window();
const window = new GlobalWindow();
for (const key of Object.keys(window)) {
if (global[key] === undefined && key !== 'undefined') {
global[key] = window[key];
Expand Down
32 changes: 26 additions & 6 deletions packages/happy-dom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ npm install happy-dom

## Basic Usage

The example below will show you how to use Happy DOM.
A simple example of how you can use Happy DOM.

```javascript
import { Window } from 'happy-dom';
Expand All @@ -79,13 +79,12 @@ console.log(document.body.innerHTML);

## VM Context

The example below will show you how to setup a Node [VM context](https://nodejs.org/api/vm.html#vm_vm_createcontext_sandbox_options) to render a page in Happy DOM. The [VM context](https://nodejs.org/api/vm.html#vm_vm_createcontext_sandbox_options) can set the Happy DOM window object to be the [global object](https://nodejs.org/api/globals.html) and allow for JavaScript code to be executed scoped within the context.
The default Window class is a [VM context](https://nodejs.org/api/vm.html#vm_vm_createcontext_sandbox_options). A [VM context](https://nodejs.org/api/vm.html#vm_vm_createcontext_sandbox_options) will execute JavaScript code scoped within the context where the Window instance will be the global object.

```javascript
import { Window } from 'happy-dom';
import VM from 'vm';

const window = VM.createContext(new Window());
const window = new Window();
const document = window.document;

window.location.href = 'http://localhost:8080';
Expand Down Expand Up @@ -113,6 +112,28 @@ document.write(`
console.log(document.querySelector('.container div').innerHTML);
```

## Global Context

Happy DOM exports a class called GlobalWindow, which can be used to run Happy DOM in the global context instead of the default behaviour of running in a [VM context](https://nodejs.org/api/vm.html#vm_vm_createcontext_sandbox_options).

```javascript
import { Window, GlobalWindow } from 'happy-dom';

const vmWindow = new Window();
const globalWindow = new GlobalWindow();

// Will output "false"
console.log(vmWindow.Array === global.Array);

// Will output "true"
console.log(globalWindow.Array === global.Array);

globalWindow.eval('global.test = 1');

// Will output "1"
console.log(global.test);
```

## Server-Side Rendering of Web Components

The example below will show you how to setup a Node [VM context](https://nodejs.org/api/vm.html#vm_vm_createcontext_sandbox_options) to render a page with custom elements (web components) in Happy DOM. We can then use a new web feature called [Declarative Shadow DOM](https://chromestatus.com/feature/5191745052606464) to include the shadow roots in the HTML output.
Expand All @@ -121,9 +142,8 @@ The example below will show you how to setup a Node [VM context](https://nodejs.

```javascript
import { Window } from 'happy-dom';
import VM from 'vm';

const window = VM.createContext(new Window());
const window = new Window();
const document = window.document;

window.location.href = 'http://localhost:8080';
Expand Down
9 changes: 4 additions & 5 deletions packages/happy-dom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@
"watch": "tsc -w --preserveWatchOutput",
"lint": "eslint --ignore-path .gitignore --max-warnings 0 .",
"lint:fix": "eslint --ignore-path .gitignore --max-warnings 0 --fix .",
"test": "jest",
"test:coverage": "jest --collectCoverage",
"test:watch": "jest --watch",
"test:update-snapshot": "jest --updateSnapshot",
"test:debug": "node --inspect-brk ./node_modules/.bin/jest --runInBand"
"test": "jest --setupFilesAfterEnv ./test/setup.js",
"test:coverage": "jest --setupFilesAfterEnv ./test/setup.js --collectCoverage",
"test:watch": "jest --setupFilesAfterEnv ./test/setup.js --watch",
"test:debug": "node --inspect-brk ./node_modules/.bin/jest --setupFilesAfterEnv ./test/setup.js --runInBand"
},
"jest": {
"transform": {
Expand Down
9 changes: 4 additions & 5 deletions packages/happy-dom/src/dom-parser/DOMParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,13 @@ export default class DOMParser {
const bodyElement = newDocument.createElement('body');
const headElement = newDocument.createElement('head');

for (const node of root.childNodes.slice()) {
bodyElement.appendChild(node);
}

documentElement.appendChild(headElement);
documentElement.appendChild(bodyElement);

newDocument.appendChild(documentElement);

for (const node of root.childNodes.slice()) {
bodyElement.appendChild(node);
}
}

return newDocument;
Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/src/event/IUIEventInit.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Window from '../window/Window';
import IWindow from '../window/IWindow';
import IEventInit from './IEventInit';

export default interface IUIEventInit extends IEventInit {
detail?: number;
view?: Window;
view?: IWindow;
}
4 changes: 2 additions & 2 deletions packages/happy-dom/src/event/UIEvent.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Window from '../window/Window';
import IWindow from '../window/IWindow';
import Event from './Event';
import IUIEventInit from './IUIEventInit';

Expand All @@ -15,7 +15,7 @@ export default class UIEvent extends Event {
public readonly layerY: number = 0;
public readonly pageX: number = 0;
public readonly pageY: number = 0;
public readonly view: Window = null;
public readonly view: IWindow = null;

/**
* Constructor.
Expand Down
6 changes: 3 additions & 3 deletions packages/happy-dom/src/fetch/FetchHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import RelativeURL from '../location/RelativeURL';
import IRequestInit from './IRequestInit';
import IDocument from '../nodes/document/IDocument';
import IResponse from './IResponse';
import Response from './Response';
import NodeFetch from 'node-fetch';

/**
* Helper class for performing fetch.
Expand All @@ -17,14 +19,12 @@ export default class FetchHandler {
*/
public static fetch(document: IDocument, url: string, init?: IRequestInit): Promise<IResponse> {
// We want to only load NodeFetch when it is needed to improve performance and not have direct dependencies to server side packages.
const nodeFetch = require('node-fetch');
const Response = require('./Response').default;
const taskManager = document.defaultView.happyDOM.asyncTaskManager;

return new Promise((resolve, reject) => {
const taskID = taskManager.startTask();

nodeFetch(RelativeURL.getAbsoluteURL(document.defaultView.location, url), init)
NodeFetch(RelativeURL.getAbsoluteURL(document.defaultView.location, url), init)
.then((response) => {
if (taskManager.getTaskCount() === 0) {
reject(new Error('Failed to complete fetch request. Task was canceled.'));
Expand Down
6 changes: 3 additions & 3 deletions packages/happy-dom/src/fetch/ResourceFetchHandler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import RelativeURL from '../location/RelativeURL';
import DOMException from '../exception/DOMException';
import IDocument from '../nodes/document/IDocument';
import SyncRequest from 'sync-request';

/**
* Helper class for performing fetch of resources.
Expand Down Expand Up @@ -32,16 +33,15 @@ export default class ResourceFetchHandler {
*/
public static fetchSync(document: IDocument, url: string): string {
// We want to only load SyncRequest when it is needed to improve performance and not have direct dependencies to server side packages.
const syncRequest = require('sync-request');
const absoluteURL = RelativeURL.getAbsoluteURL(document.defaultView.location, url);
const response = syncRequest('GET', absoluteURL);
const response = SyncRequest('GET', absoluteURL);

if (response.isError()) {
throw new DOMException(
`Failed to perform request to "${absoluteURL}". Status code: ${response.statusCode}`
);
}

return response.getBody();
return response.getBody().toString();
}
}
6 changes: 3 additions & 3 deletions packages/happy-dom/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Window from './window/Window';
import GlobalWindow from './window/GlobalWindow';
import IWindow from './window/IWindow';
import Window from './window/Window';
import DataTransfer from './event/DataTransfer';
import DataTransferItem from './event/DataTransferItem';
import DataTransferItemList from './event/DataTransferItemList';
Expand Down Expand Up @@ -40,7 +41,6 @@ import KeyboardEvent from './event/events/KeyboardEvent';
import MouseEvent from './event/events/MouseEvent';
import ProgressEvent from './event/events/ProgressEvent';
import WheelEvent from './event/events/WheelEvent';
import AsyncWindow from './window/AsyncWindow';
import DOMParser from './dom-parser/DOMParser';
import Document from './nodes/document/Document';
import IDocument from './nodes/document/IDocument';
Expand Down Expand Up @@ -105,7 +105,7 @@ import { URLSearchParams } from 'url';
import Selection from './selection/Selection';

export {
AsyncWindow,
GlobalWindow,
Window,
IWindow,
DataTransfer,
Expand Down
8 changes: 4 additions & 4 deletions packages/happy-dom/src/nodes/document/Document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Element from '../element/Element';
import HTMLUnknownElement from '../html-unknown-element/HTMLUnknownElement';
import Text from '../text/Text';
import Comment from '../comment/Comment';
import Window from '../../window/Window';
import IWindow from '../../window/IWindow';
import Node from '../node/Node';
import TreeWalker from '../../tree-walker/TreeWalker';
import DocumentFragment from '../document-fragment/DocumentFragment';
Expand Down Expand Up @@ -54,7 +54,7 @@ export default class Document extends Node implements IDocument {
public _activeElement: IHTMLElement = null;
protected _isFirstWrite = true;
protected _isFirstWriteAfterOpen = false;
private _defaultView: Window = null;
private _defaultView: IWindow = null;
private _cookie = '';

/**
Expand Down Expand Up @@ -103,7 +103,7 @@ export default class Document extends Node implements IDocument {
*
* @returns Default view.
*/
public get defaultView(): Window {
public get defaultView(): IWindow {
return this._defaultView;
}

Expand All @@ -112,7 +112,7 @@ export default class Document extends Node implements IDocument {
*
* @param defaultView Default view.
*/
public set defaultView(defaultView: Window) {
public set defaultView(defaultView: IWindow) {
this._defaultView = defaultView;
this._readyStateManager = new DocumentReadyStateManager(defaultView);
this._readyStateManager.whenComplete().then(() => {
Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/src/nodes/document/IDocument.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import IElement from '../element/IElement';
import IHTMLElement from '../html-element/IHTMLElement';
import Window from '../../window/Window';
import IWindow from '../../window/IWindow';
import TreeWalker from '../../tree-walker/TreeWalker';
import Event from '../../event/Event';
import DOMImplementation from '../../dom-implementation/DOMImplementation';
Expand All @@ -24,7 +24,7 @@ import INodeList from '../node/INodeList';
*/
export default interface IDocument extends IParentNode {
onreadystatechange: (event: Event) => void;
readonly defaultView: Window;
readonly defaultView: IWindow;
readonly implementation: DOMImplementation;
readonly documentElement: IHTMLElement;
readonly doctype: IDocumentType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,10 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
})
);
(<Document>this.ownerDocument)._readyStateManager.endTask();
if (!this._listeners['error'] && !this.ownerDocument.defaultView._listeners['error']) {
if (
!this['_listeners']['error'] &&
!this.ownerDocument.defaultView['_listeners']['error']
) {
this.ownerDocument.defaultView.console.error(error);
}
});
Expand Down Expand Up @@ -267,7 +270,10 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
})
);
(<Document>this.ownerDocument)._readyStateManager.endTask();
if (!this._listeners['error'] && !this.ownerDocument.defaultView._listeners['error']) {
if (
!this['_listeners']['error'] &&
!this.ownerDocument.defaultView['_listeners']['error']
) {
this.ownerDocument.defaultView.console.error(error);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export default class ScriptUtility {
})
);
if (
!element._listeners['error'] &&
!element.ownerDocument.defaultView._listeners['error']
!element['_listeners']['error'] &&
!element.ownerDocument.defaultView['_listeners']['error']
) {
element.ownerDocument.defaultView.console.error(error);
}
Expand Down Expand Up @@ -66,8 +66,8 @@ export default class ScriptUtility {
})
);
if (
!element._listeners['error'] &&
!element.ownerDocument.defaultView._listeners['error']
!element['_listeners']['error'] &&
!element.ownerDocument.defaultView['_listeners']['error']
) {
element.ownerDocument.defaultView.console.error(error);
}
Expand Down
17 changes: 0 additions & 17 deletions packages/happy-dom/src/window/AsyncWindow.ts

This file was deleted.