Skip to content

Commit

Permalink
Merge pull request #458 from capricorn86/task/454-implement-vm-inside…
Browse files Browse the repository at this point in the history
…-happy-dom

Task/454 implement vm inside happy dom
  • Loading branch information
capricorn86 committed Apr 26, 2022
2 parents faaaa2c + 0957ed5 commit e3f1d5c
Show file tree
Hide file tree
Showing 29 changed files with 685 additions and 527 deletions.
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.

0 comments on commit e3f1d5c

Please sign in to comment.