diff --git a/package-lock.json b/package-lock.json index abad5394c..5ae53c2b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3680,6 +3680,25 @@ "node": ">=0.10.0" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -3805,6 +3824,29 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -6397,6 +6439,10 @@ "resolved": "packages/happy-dom", "link": true }, + "node_modules/happy-dom-without-nodejs": { + "resolved": "packages/happy-dom-without-nodejs", + "link": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -6698,6 +6744,25 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -9409,10 +9474,9 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } @@ -10912,6 +10976,17 @@ "node": ">=6" } }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ts-jest": { "version": "29.1.1", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", @@ -11725,6 +11800,18 @@ "node": ">=12" } }, + "node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -11925,7 +12012,6 @@ "dependencies": { "css.escape": "^1.5.1", "entities": "^4.5.0", - "iconv-lite": "^0.6.3", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^2.0.0", "whatwg-mimetype": "^3.0.0" @@ -11943,6 +12029,31 @@ "vitest": "^0.32.4" } }, + "packages/happy-dom-without-nodejs": { + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "css.escape": "^1.5.1", + "entities": "^4.5.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^14.0.0" + }, + "devDependencies": { + "@types/css.escape": "^1.5.0", + "@types/node": "^16.11.7", + "@types/node-fetch": "^2.6.1", + "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/parser": "^5.16.0", + "@vitest/ui": "^0.33.0", + "@webref/css": "6.6.2", + "prettier": "^2.6.0", + "typescript": "^5.0.4", + "vitest": "^0.32.4" + } + }, "packages/integration-test": { "name": "@happy-dom/integration-test", "version": "0.0.0", diff --git a/packages/happy-dom-without-nodejs/.editorconfig b/packages/happy-dom-without-nodejs/.editorconfig new file mode 100755 index 000000000..ec8afa677 --- /dev/null +++ b/packages/happy-dom-without-nodejs/.editorconfig @@ -0,0 +1,8 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Indentation override for all JS under lib directory +[{**.js,**.ts,**.json}] +indent_style = tab \ No newline at end of file diff --git a/packages/happy-dom-without-nodejs/.gitignore b/packages/happy-dom-without-nodejs/.gitignore new file mode 100755 index 000000000..48f00e915 --- /dev/null +++ b/packages/happy-dom-without-nodejs/.gitignore @@ -0,0 +1,6 @@ +node_modules +tmp +lib +src +cjs +.DS_Store \ No newline at end of file diff --git a/packages/happy-dom-without-nodejs/.npmignore b/packages/happy-dom-without-nodejs/.npmignore new file mode 100644 index 000000000..630ba6a00 --- /dev/null +++ b/packages/happy-dom-without-nodejs/.npmignore @@ -0,0 +1,7 @@ +node_modules +tmp +test +bin +polyfills +.turbo +npm-shrinkwrap.json \ No newline at end of file diff --git a/packages/happy-dom-without-nodejs/.prettierrc.cjs b/packages/happy-dom-without-nodejs/.prettierrc.cjs new file mode 100644 index 000000000..e66b0dcab --- /dev/null +++ b/packages/happy-dom-without-nodejs/.prettierrc.cjs @@ -0,0 +1,6 @@ +module.exports = { + singleQuote: true, + useTabs: true, + printWidth: 100, + trailingComma: 'none' +}; \ No newline at end of file diff --git a/packages/happy-dom-without-nodejs/LICENSE b/packages/happy-dom-without-nodejs/LICENSE new file mode 100644 index 000000000..eebd5782b --- /dev/null +++ b/packages/happy-dom-without-nodejs/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 David Ortner (capricorn86) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/happy-dom-without-nodejs/README.md b/packages/happy-dom-without-nodejs/README.md new file mode 100644 index 000000000..20e55fbe4 --- /dev/null +++ b/packages/happy-dom-without-nodejs/README.md @@ -0,0 +1,131 @@ +![Happy DOM Logo](https://github.com/capricorn86/happy-dom/raw/master/docs/happy-dom-logo.jpg) + +# About + +[Happy DOM](https://github.com/capricorn86/happy-dom) is a JavaScript implementation of a web browser without its graphical user interface. It includes many web standards from WHATWG [DOM](https://dom.spec.whatwg.org/) and [HTML](https://html.spec.whatwg.org/multipage/). + +The goal of [Happy DOM](https://github.com/capricorn86/happy-dom) is to emulate enough of a web browser to be useful for testing, scraping web sites and server-side rendering. + +[Happy DOM](https://github.com/capricorn86/happy-dom) focuses heavily on performance and can be used as an alternative to [JSDOM](https://github.com/jsdom/jsdom). + +### DOM Features + +- Custom Elements (Web Components) + +- Shadow Root (Shadow DOM) + +- Declarative Shadow DOM + +- Mutation Observer + +- Tree Walker + +- Fetch + +And much more.. + +### Works With + +- [Google LitHTML](https://lit-html.polymer-project.org) + +- [Google LitElement](https://lit-element.polymer-project.org) + +- [React](https://reactjs.org) + +- [Angular](https://angular.io/) + +- [Vue](https://vuejs.org/) + +### Module Systems + +- [ESM](https://nodejs.org/api/esm.html#introduction) +- [CommonJS](https://nodejs.org/api/modules.html#modules-commonjs-modules) + +# Installation + +```bash +npm install happy-dom +``` + +# Usage + +Happy DOM can be used as a simulated [Browser](https://github.com/capricorn86/happy-dom/wiki/Browser) or by using the [Window](https://github.com/capricorn86/happy-dom/wiki/Window) class directly to quickly setup up a DOM. + +## Window + +```javascript +import { Window } from 'happy-dom'; + +const window = new Window({ url: 'https://localhost:8080' }); +const document = window.document; + +document.body.innerHTML = '
'; + +const container = document.querySelector('.container'); +const button = document.createElement('button'); + +container.appendChild(button); + +// Outputs "
" +console.log(document.body.innerHTML); +``` + +## Browser + +```javascript +import { Browser, BrowserErrorCaptureEnum } from 'happy-dom'; + +const browser = new Browser({ settings: { errorCapture: BrowserErrorCaptureEnum.processLevel } }); +const page = browser.newPage(); + +// Navigates page +await page.goto('https://github.com/capricorn86'); + +// Clicks on link +page.mainFrame.document.querySelector('a[href*="capricorn86/happy-dom"]').click(); + +// Waits for all operations on the page to complete (fetch, timers etc.) +await page.waitUntilComplete(); + +// Outputs "GitHub - capricorn86/happy-dom: Happy DOM..." +console.log(page.mainFrame.document.title); + +// Closes the browser +await browser.close(); +``` + +# Documentation + +Read more about how to use Happy DOM in our [Wiki](https://github.com/capricorn86/happy-dom/wiki). + +# Performance + +| Operation | JSDOM | Happy DOM | +| ------------------------------------ | ------- | --------- | +| Import / Require | 333 ms | 45 ms | +| Parse HTML | 256 ms | 26 ms | +| Serialize HTML | 65 ms | 8 ms | +| Render custom element | 214 ms | 19 ms | +| querySelectorAll('tagname') | 4.9 ms | 0.7 ms | +| querySelectorAll('.class') | 6.4 ms | 3.7 ms | +| querySelectorAll('[attribute]') | 4.0 ms | 1.7 ms | +| querySelectorAll('[class~="name"]') | 5.5 ms | 2.9 ms | +| querySelectorAll(':nth-child(2n+1)') | 10.4 ms | 3.8 ms | + +See how the test was done [here](https://github.com/capricorn86/happy-dom-performance-test) + +# Jest + +Happy DOM provide with a package called [@happy-dom/jest-environment](https://github.com/capricorn86/happy-dom/tree/master/packages/jest-environment) that makes it possible to use Happy DOM with [Jest](https://jestjs.io/). + +# Vitest + +[Vitest](https://github.com/vitest-dev/vitest) supports Happy DOM out of the box. + +# Global Registration + +Happy DOM provide with a package called [@happy-dom/global-registrator](https://github.com/capricorn86/happy-dom/tree/master/packages/global-registrator) that can register Happy DOM globally. It makes it possible to use Happy DOM for testing in a Node environment. + +# Sponsors + +[RTVision](https://rtvision.com) diff --git a/packages/happy-dom-without-nodejs/bin/polyfill-source.cjs b/packages/happy-dom-without-nodejs/bin/polyfill-source.cjs new file mode 100644 index 000000000..bbd12f178 --- /dev/null +++ b/packages/happy-dom-without-nodejs/bin/polyfill-source.cjs @@ -0,0 +1,138 @@ +/* eslint-disable no-console*/ +/* eslint-disable @typescript-eslint/no-var-requires*/ + +const Path = require('path'); +const FS = require('fs'); + +process.on('unhandledRejection', (reason) => { + console.error(reason); + process.exit(1); +}); + +main(); + +const POLYFILL_MODULES = ['net', 'crypto', 'url', 'stream', 'vm']; + +const POLYFILLS = [ + function polyfillProcess(_directory, file, content) { + if (!file.endsWith('.cjs') && !file.endsWith('.js')) { + return content; + } + return content + .replace(/process\.platform/gm, `'Unknown'`) + .replace(/process\.arch/gm, `'Unknown'`); + }, + function polyfillNodeJS(_directory, _file, content) { + return content.replace(/NodeJS.Timeout/gm, `number`).replace(/NodeJS.Immediate/gm, `number`); + }, + function polyfillFetch(_directory, file, content) { + if (file.endsWith('/Fetch.d.ts') || file.endsWith('/SyncFetch.d.ts')) { + return `export default class Fetch { send(): Promise; }`; + } + if (file.endsWith('/Fetch.js') || file.endsWith('/SyncFetch.js')) { + return `export default class Fetch { send() { throw Error('Fetch is not supported without Node.js.'); } }`; + } + if (file.endsWith('/Fetch.cjs') || file.endsWith('/SyncFetch.cjs')) { + return `class Fetch { send() { throw Error('Fetch is not supported without Node.js.'); } }\n\nmodule.exports = Fetch;\nmodule.exports.default = Fetch;`; + } + return content; + }, + function polyfillTypescriptDefinition(_directory, file, content) { + if (!file.endsWith('.d.ts')) { + return content; + } + + return content.replace( + /\/\/\/\s*/gm, + '' + ); + }, + function polyfillModules(directory, file, content) { + const polyfillDirectory = Path.join(directory, 'polyfills'); + for (const module of POLYFILL_MODULES) { + const regexp = new RegExp(`import.+from\\s*(["']${module}["'])`); + moduleMatch = content.match(regexp); + if (moduleMatch) { + content = content.replace( + moduleMatch[0], + moduleMatch[0].replace( + moduleMatch[1] || moduleMatch[2], + `'${Path.relative(Path.dirname(file), Path.join(polyfillDirectory, `${module}.js`))}'` + ) + ); + } + } + return content; + } +]; + +function getArguments() { + const args = { + dir: null + }; + + for (const arg of process.argv) { + if (arg.startsWith('--dir=')) { + args.dir = arg.split('=')[1]; + } + } + + return args; +} + +async function readDirectory(directory) { + const files = await FS.promises.readdir(directory); + const statsPromises = []; + let allFiles = []; + + for (const file of files) { + const filePath = Path.join(directory, file); + statsPromises.push( + FS.promises.stat(filePath).then((stats) => { + if (stats.isDirectory()) { + return readDirectory(filePath).then((files) => (allFiles = allFiles.concat(files))); + } + const extname = Path.extname(filePath); + if (extname === '.js' || extname === '.cjs' || extname === '.ts') { + allFiles.push(filePath); + } + }) + ); + } + + await Promise.all(statsPromises); + + return allFiles; +} + +async function polyfillFiles(directory, files) { + const writePromises = []; + + for (const file of files) { + writePromises.push( + FS.promises.readFile(file).then((content) => { + const oldContent = content.toString(); + let newContent = oldContent; + for (const polyfill of POLYFILLS) { + newContent = polyfill(directory, file, newContent); + } + if (newContent === oldContent) { + return; + } + return FS.promises.writeFile(file, newContent); + }) + ); + } + + await Promise.all(writePromises); +} + +async function main() { + const args = getArguments(); + if (!args.dir) { + throw new Error('Invalid arguments'); + } + const directory = Path.resolve(args.dir); + const files = await readDirectory(directory); + await polyfillFiles(directory, files); +} diff --git a/packages/happy-dom-without-nodejs/package-lock.json b/packages/happy-dom-without-nodejs/package-lock.json new file mode 100644 index 000000000..35ccb79a0 --- /dev/null +++ b/packages/happy-dom-without-nodejs/package-lock.json @@ -0,0 +1,30 @@ +{ + "name": "happy-dom", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "happy-dom", + "version": "0.0.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^18.11.15" + } + }, + "node_modules/@types/node": { + "version": "18.11.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.15.tgz", + "integrity": "sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw==", + "dev": true + } + }, + "dependencies": { + "@types/node": { + "version": "18.11.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.15.tgz", + "integrity": "sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw==", + "dev": true + } + } +} diff --git a/packages/happy-dom-without-nodejs/package.json b/packages/happy-dom-without-nodejs/package.json new file mode 100644 index 000000000..5f2ec2f51 --- /dev/null +++ b/packages/happy-dom-without-nodejs/package.json @@ -0,0 +1,74 @@ +{ + "name": "happy-dom-without-nodejs", + "version": "0.0.0", + "license": "MIT", + "homepage": "https://github.com/capricorn86/happy-dom/packages/happy-dom-without-nodejs", + "repository": "https://github.com/capricorn86/happy-dom", + "author": "David Ortner", + "description": "Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. It includes many web standards from WHATWG DOM and HTML.", + "main": "lib/index.js", + "type": "module", + "exports": { + ".": { + "import": "./lib/index.js", + "default": "./lib/index.js" + }, + "./lib/*.js": { + "import": "./lib/*.js", + "default": "./lib/*.js" + }, + "./lib/*.ts": { + "import": "./lib/*.ts", + "default": "./lib/*.ts" + }, + "./lib/*.map": { + "import": "./lib/*.map", + "default": "./lib/*.map" + }, + "./src/*.ts": "./src/*.ts", + "./package.json": "./package.json", + "./.eslintrc.cjs": "./.eslintrc.cjs", + "./.prettierrc.cjs": "./.prettierrc.cjs" + }, + "keywords": [ + "jsdom", + "dom", + "browser", + "custom", + "elements", + "web", + "components", + "html", + "whatwg", + "w3c" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "compile": "rm -rf ./lib ./src && cp -r ../happy-dom/lib ./lib && cp -r ../happy-dom/src ./src && cp -r ./polyfills ./lib && npm run polyfill", + "polyfill": "node ./bin/polyfill-source.cjs --dir=./lib", + "test": "tsc --noEmit" + }, + "dependencies": { + "css.escape": "^1.5.1", + "entities": "^4.5.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^14.0.0", + "buffer": "^6.0.3" + }, + "devDependencies": { + "@types/css.escape": "^1.5.0", + "@types/node": "^16.11.7", + "@types/node-fetch": "^2.6.1", + "@typescript-eslint/eslint-plugin": "^5.16.0", + "@typescript-eslint/parser": "^5.16.0", + "@vitest/ui": "^0.33.0", + "@webref/css": "6.6.2", + "prettier": "^2.6.0", + "typescript": "^5.0.4", + "vitest": "^0.32.4" + } +} diff --git a/packages/happy-dom-without-nodejs/polyfills/crypto.js b/packages/happy-dom-without-nodejs/polyfills/crypto.js new file mode 100644 index 000000000..a7c1a183b --- /dev/null +++ b/packages/happy-dom-without-nodejs/polyfills/crypto.js @@ -0,0 +1,3 @@ +const webcrypto = globalThis.crypto; + +export { webcrypto }; diff --git a/packages/happy-dom-without-nodejs/polyfills/net.js b/packages/happy-dom-without-nodejs/polyfills/net.js new file mode 100644 index 000000000..ec5b8c0fe --- /dev/null +++ b/packages/happy-dom-without-nodejs/polyfills/net.js @@ -0,0 +1,18 @@ +function isIP(ip) { + const ipv4 = + /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + const ipv6 = + /^(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))$/; + + if (ipv4.test(ip)) { + return 4; + } + + if (ipv6.test(ip)) { + return 6; + } + + return 0; +} + +export { isIP }; diff --git a/packages/happy-dom-without-nodejs/polyfills/stream.js b/packages/happy-dom-without-nodejs/polyfills/stream.js new file mode 100644 index 000000000..cb81dbbd9 --- /dev/null +++ b/packages/happy-dom-without-nodejs/polyfills/stream.js @@ -0,0 +1,10 @@ +const Readable = globalThis.ReadableStream; +const Writable = globalThis.WritableStream; +const Transform = globalThis.TransformStream; + +export { Readable, Writable, Transform }; +export default { + Readable, + Writable, + Transform +}; diff --git a/packages/happy-dom-without-nodejs/polyfills/url.js b/packages/happy-dom-without-nodejs/polyfills/url.js new file mode 100644 index 000000000..a3eca0d9e --- /dev/null +++ b/packages/happy-dom-without-nodejs/polyfills/url.js @@ -0,0 +1,3 @@ +import { URL, URLSearchParams, UrlObject } from 'whatwg-url'; + +export { URL, URLSearchParams, UrlObject }; diff --git a/packages/happy-dom-without-nodejs/polyfills/vm.js b/packages/happy-dom-without-nodejs/polyfills/vm.js new file mode 100644 index 000000000..05800614c --- /dev/null +++ b/packages/happy-dom-without-nodejs/polyfills/vm.js @@ -0,0 +1,12 @@ +/** + * + */ +class Script { + /** + * + */ + runInContext() {} +} +const isContext = () => true; +const createContext = (context) => context; +export { Script, isContext, createContext }; diff --git a/packages/happy-dom-without-nodejs/tsconfig.json b/packages/happy-dom-without-nodejs/tsconfig.json new file mode 100644 index 000000000..1aaa25cfe --- /dev/null +++ b/packages/happy-dom-without-nodejs/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "rootDir": "lib", + "target": "ES2020", + "declaration": true, + "declarationMap": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "esModuleInterop": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "removeComments": false, + "preserveConstEnums": true, + "sourceMap": true, + "skipLibCheck": true, + "baseUrl": ".", + "composite": false, + "incremental": false, + "allowJs": true, + "lib": [ + "es2020", + "dom" + ], + "types": [] + }, + "include": [ + "lib" + ], + "exclude": [ + "@types/dom" + ] +} \ No newline at end of file diff --git a/packages/happy-dom-without-nodejs/vitest.config.ts b/packages/happy-dom-without-nodejs/vitest.config.ts new file mode 100644 index 000000000..554c98429 --- /dev/null +++ b/packages/happy-dom-without-nodejs/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + include: ['./test/**/*.test.ts'], + setupFiles: ['./test/setup.ts'], + testTimeout: 500 + } +}); diff --git a/packages/happy-dom/README.md b/packages/happy-dom/README.md index 741ed234b..20e55fbe4 100644 --- a/packages/happy-dom/README.md +++ b/packages/happy-dom/README.md @@ -49,7 +49,7 @@ npm install happy-dom # Usage -A simple example of how you can use Happy DOM. +Happy DOM can be used as a simulated [Browser](https://github.com/capricorn86/happy-dom/wiki/Browser) or by using the [Window](https://github.com/capricorn86/happy-dom/wiki/Window) class directly to quickly setup up a DOM. ## Window diff --git a/packages/happy-dom/package.json b/packages/happy-dom/package.json index 1d43d30fe..331fb575b 100644 --- a/packages/happy-dom/package.json +++ b/packages/happy-dom/package.json @@ -77,7 +77,6 @@ "dependencies": { "css.escape": "^1.5.1", "entities": "^4.5.0", - "iconv-lite": "^0.6.3", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^2.0.0", "whatwg-mimetype": "^3.0.0" diff --git a/packages/happy-dom/src/console/VirtualConsole.ts b/packages/happy-dom/src/console/VirtualConsole.ts index 56232288b..d227124d7 100644 --- a/packages/happy-dom/src/console/VirtualConsole.ts +++ b/packages/happy-dom/src/console/VirtualConsole.ts @@ -2,7 +2,6 @@ import IVirtualConsolePrinter from './types/IVirtualConsolePrinter.js'; import VirtualConsoleLogLevelEnum from './enums/VirtualConsoleLogLevelEnum.js'; import VirtualConsoleLogTypeEnum from './enums/VirtualConsoleLogTypeEnum.js'; import IVirtualConsoleLogGroup from './types/IVirtualConsoleLogGroup.js'; -import * as PerfHooks from 'perf_hooks'; import { ConsoleConstructor } from 'console'; /** @@ -276,7 +275,7 @@ export default class VirtualConsole implements Console { * @param [label=default] Label. */ public time(label = 'default'): void { - this.#time[label] = PerfHooks.performance.now(); + this.#time[label] = performance.now(); } /** @@ -288,7 +287,7 @@ export default class VirtualConsole implements Console { public timeEnd(label = 'default'): void { const time = this.#time[label]; if (time) { - const duration = PerfHooks.performance.now() - time; + const duration = performance.now() - time; this.#printer.print({ type: VirtualConsoleLogTypeEnum.timeEnd, level: VirtualConsoleLogLevelEnum.info, @@ -308,7 +307,7 @@ export default class VirtualConsole implements Console { public timeLog(label = 'default', ...args: Array): void { const time = this.#time[label]; if (time) { - const duration = PerfHooks.performance.now() - time; + const duration = performance.now() - time; this.#printer.print({ type: VirtualConsoleLogTypeEnum.timeLog, level: VirtualConsoleLogLevelEnum.info, diff --git a/packages/happy-dom/src/event/Event.ts b/packages/happy-dom/src/event/Event.ts index 2ed9e5ca9..e76d6cfa9 100644 --- a/packages/happy-dom/src/event/Event.ts +++ b/packages/happy-dom/src/event/Event.ts @@ -5,7 +5,6 @@ import IBrowserWindow from '../window/IBrowserWindow.js'; import IShadowRoot from '../nodes/shadow-root/IShadowRoot.js'; import IEventTarget from './IEventTarget.js'; import NodeTypeEnum from '../nodes/node/NodeTypeEnum.js'; -import { performance } from 'perf_hooks'; import EventPhaseEnum from './EventPhaseEnum.js'; import IDocument from '../nodes/document/IDocument.js'; diff --git a/packages/happy-dom/src/window/BrowserWindow.ts b/packages/happy-dom/src/window/BrowserWindow.ts index b45136935..6f95d13a7 100644 --- a/packages/happy-dom/src/window/BrowserWindow.ts +++ b/packages/happy-dom/src/window/BrowserWindow.ts @@ -98,7 +98,6 @@ import PluginArray from '../navigator/PluginArray.js'; import Fetch from '../fetch/Fetch.js'; import DOMRect from '../nodes/element/DOMRect.js'; import VMGlobalPropertyScript from './VMGlobalPropertyScript.js'; -import * as PerfHooks from 'perf_hooks'; import VM from 'vm'; import { Buffer } from 'buffer'; import { webcrypto } from 'crypto'; @@ -418,12 +417,12 @@ export default class BrowserWindow extends EventTarget implements IBrowserWindow public readonly screen: Screen; public readonly sessionStorage: Storage; public readonly localStorage: Storage; - public readonly performance = PerfHooks.performance; + public readonly performance: typeof performance = performance; public readonly screenLeft: number = 0; public readonly screenTop: number = 0; public readonly screenX: number = 0; public readonly screenY: number = 0; - public readonly crypto = webcrypto; + public readonly crypto: typeof webcrypto = webcrypto; public readonly closed = false; public name = ''; @@ -431,7 +430,7 @@ export default class BrowserWindow extends EventTarget implements IBrowserWindow public Array: typeof Array; public ArrayBuffer: typeof ArrayBuffer; public Boolean: typeof Boolean; - public Buffer = Buffer; + public Buffer: typeof Buffer = Buffer; public DataView: typeof DataView; public Date: typeof Date; public Error: typeof Error; diff --git a/packages/happy-dom/src/window/GlobalWindow.ts b/packages/happy-dom/src/window/GlobalWindow.ts index 0769e3eb3..437be0edf 100644 --- a/packages/happy-dom/src/window/GlobalWindow.ts +++ b/packages/happy-dom/src/window/GlobalWindow.ts @@ -14,7 +14,7 @@ export default class GlobalWindow extends Window implements IWindow { public Array: typeof Array = globalThis.Array; public ArrayBuffer: typeof ArrayBuffer = globalThis.ArrayBuffer; public Boolean: typeof Boolean = globalThis.Boolean; - public Buffer = Buffer; + public Buffer: typeof Buffer = Buffer; public DataView: typeof DataView = globalThis.DataView; public Date: typeof Date = globalThis.Date; public Error: typeof Error = globalThis.Error; diff --git a/packages/happy-dom/src/window/IBrowserWindow.ts b/packages/happy-dom/src/window/IBrowserWindow.ts index 3df28a7ee..fcc188d79 100644 --- a/packages/happy-dom/src/window/IBrowserWindow.ts +++ b/packages/happy-dom/src/window/IBrowserWindow.ts @@ -108,7 +108,6 @@ import XMLHttpRequestEventTarget from '../xml-http-request/XMLHttpRequestEventTa import DOMRect from '../nodes/element/DOMRect.js'; import Attr from '../nodes/attr/Attr.js'; import NamedNodeMap from '../named-node-map/NamedNodeMap.js'; -import { Performance } from 'perf_hooks'; import IElement from '../nodes/element/IElement.js'; import SVGGraphicsElement from '../nodes/svg-element/SVGGraphicsElement.js'; import ProcessingInstruction from '../nodes/processing-instruction/ProcessingInstruction.js'; @@ -409,7 +408,7 @@ export default interface IBrowserWindow extends IEventTarget, INodeJSGlobal { readonly screenY: number; readonly sessionStorage: Storage; readonly localStorage: Storage; - readonly performance: Performance; + readonly performance: typeof performance; readonly pageXOffset: number; readonly pageYOffset: number; readonly scrollX: number; diff --git a/packages/happy-dom/src/window/INodeJSGlobal.ts b/packages/happy-dom/src/window/INodeJSGlobal.ts index c1960be52..e1559d871 100644 --- a/packages/happy-dom/src/window/INodeJSGlobal.ts +++ b/packages/happy-dom/src/window/INodeJSGlobal.ts @@ -1,3 +1,5 @@ +import { Buffer } from 'buffer'; + /* eslint-disable @typescript-eslint/no-explicit-any */ export default interface INodeJSGlobal { diff --git a/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts b/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts index 12066da3c..4bc42266b 100644 --- a/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts +++ b/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts @@ -21,7 +21,7 @@ import AbortController from '../fetch/AbortController.js'; import ProgressEvent from '../event/events/ProgressEvent.js'; import NodeTypeEnum from '../nodes/node/NodeTypeEnum.js'; import IRequestBody from '../fetch/types/IRequestBody.js'; -import XMLHttpRequestResponseDataParser from './utilities/XMLHttpRequestResponseDataParser.js'; +import XMLHttpRequestResponseDataParser from './XMLHttpRequestResponseDataParser.js'; import FetchRequestHeaderUtility from '../fetch/utilities/FetchRequestHeaderUtility.js'; /** diff --git a/packages/happy-dom/src/xml-http-request/XMLHttpRequestCertificate.ts b/packages/happy-dom/src/xml-http-request/XMLHttpRequestCertificate.ts deleted file mode 100644 index af8d7744e..000000000 --- a/packages/happy-dom/src/xml-http-request/XMLHttpRequestCertificate.ts +++ /dev/null @@ -1,52 +0,0 @@ -// SSL certificate generated for Happy DOM to be able to perform HTTPS requests -export default { - cert: `-----BEGIN CERTIFICATE----- -MIIDYzCCAkugAwIBAgIUJRKB/H66hpet1VfUlm0CiXqePA4wDQYJKoZIhvcNAQEL -BQAwQTELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVNrYW5lMQ4wDAYDVQQHDAVNYWxt -bzESMBAGA1UECgwJSGFwcHkgRE9NMB4XDTIyMTAxMTIyMDM0OVoXDTMyMTAwODIy -MDM0OVowQTELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVNrYW5lMQ4wDAYDVQQHDAVN -YWxtbzESMBAGA1UECgwJSGFwcHkgRE9NMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAqerQSQEg/SxVxRiwlItithr5e5EMZo1nsqt/xOxagbmpW3IEmt0j -bpbH7iEF4DDEo7KAOwUCOwVWeFxRoag8lG2ax48wrgjlCna45XDn0Xeg1ARajL04 -gs46HZ0VrzIloVGfln0zgt/Vum5BNqs9Oc5fQoBmoP3cAn3dn4ZVcP0AKthtcyPl -q2DuNRN0PV0D2RtMSiAy9l1Ko6N5x+sAeClDyOL+sTDLngZBVeZyOKt9Id15S8Zt -XtA6VMgHnnF3jChn7pag77rsd/y5iANAVNZYqRl+Eg7xaDcsvbgH46UBOrBcB39Q -tTh5Mtjoxep5e3ZDFG+kQ1HUE+iz5O5n0wIDAQABo1MwUTAdBgNVHQ4EFgQU69s9 -YSobG/m2SN4L/7zTaF7iDbwwHwYDVR0jBBgwFoAU69s9YSobG/m2SN4L/7zTaF7i -DbwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAi/WUXx2oal8L -YnPlIuKfh49n/K18wXSYG//oFYwxfVxqpYH8hUiXVm/GUcXCxS++hUkaKLqXmH9q -MKJiCrZr3vS+2nsBKopkICu/TLdROl0sAI9lByfnEbfSAzjxe1IWJdK8NdY0y5m5 -9pEr/URVIAp/CxrneyASb4q0Jg5To3FR7vYc+2X6wZn0MundKMg6Dp9/A37jiF3l -Tt/EJp299YZcsUzh+LnRuggRjnoOVu1aLcLFlaUiwZfy9m8mLG6B/mdW/qNzNMh9 -Oqvg1zfGdpz/4D/2UUUBn6pq1vbsoAaF3OesoA3mfDcegDf/H9woJlpT0Wql+e68 -Y3FblSokcA== ------END CERTIFICATE-----`, - key: `-----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCp6tBJASD9LFXF -GLCUi2K2Gvl7kQxmjWeyq3/E7FqBualbcgSa3SNulsfuIQXgMMSjsoA7BQI7BVZ4 -XFGhqDyUbZrHjzCuCOUKdrjlcOfRd6DUBFqMvTiCzjodnRWvMiWhUZ+WfTOC39W6 -bkE2qz05zl9CgGag/dwCfd2fhlVw/QAq2G1zI+WrYO41E3Q9XQPZG0xKIDL2XUqj -o3nH6wB4KUPI4v6xMMueBkFV5nI4q30h3XlLxm1e0DpUyAeecXeMKGfulqDvuux3 -/LmIA0BU1lipGX4SDvFoNyy9uAfjpQE6sFwHf1C1OHky2OjF6nl7dkMUb6RDUdQT -6LPk7mfTAgMBAAECggEAKkwTkTjAt4UjzK56tl+EMQTB+ep/hb/JgoaChci4Nva6 -m9LkJpDJ0yuhlTuPNOGu8XjrxsVWas7HWarRf0Zb3i7yip6wZYI9Ub+AA015x4DZ -/i0fRU2NFbK0cM67qSL4jxG8gj+kZP3HPGNZxHwX/53JxMolwgmvjMc8NgvAlSFd -NnV9h4xtbhUh1NGS5zmP3iU2rwnE8JrIEzwy6axLom7nekAgkdcbAr0UoBs8gcgH -aYNhU4Gz3tGcZZ0IXAfT/bJIH1Ko8AGv4pssWc3BXcmmNdm/+kzvHIxEIV7Qegmo -XG1ZyZCyD/0b4/3e8ySDBEDqwR+HeyTW2isWG2agAQKBgQDp44aTwr3dkIXY30xv -FPfUOipg/B49dWnffYJ9MWc1FT9ijNPAngWSk0EIiEQIazICcUBI4Yji6/KeyqLJ -GdLpDi1CkKqtyh73mjELinYp3EUQgEa77aQogGa2+nMOVfu+O5CtloUrv/A18jX3 -+VEyaEASK0fWmnSI0OdlxQHIAQKBgQC5+xOls2F3MlKASvWRLlnW1wHqlDTtVoYg -5Nh8syZH4Ci2UH8tON3A5/7SWNM0t1cgV6Cw4zW8Z2spgIT/W0iYYrQ4hHL1xdCu -+CxL1km4Gy8Uwpsd+KdFahFqF/XTmLzW0HXLxWSK0fTwmdV0SFrKF3MXfTCU2AeZ -jJoMFb6P0wKBgQC3Odw6s0vkYAzLGhuZxfZkVvDOK5RRF0NKpttr0iEFL9EJFkPo -2KKK8jr3QTDy229BBJGUxsJi6u6VwS8HlehpVQbV59kd7oKV/EBBx0XMg1fDlopT -PNbmN7i/zbIG4AsoOyebJZjL7kBzMn1e9vzKHWtcEHXlw/hZGja8vjooAQKBgAeg -xK2HLfg1mCyq5meN/yFQsENu0LzrT5UJzddPgcJw7zqLEqxIKNBAs7Ls8by3yFsL -PQwERa/0jfCl1M6kb9XQNpQa2pw6ANUsWKTDpUJn2wZ+9N3F1RaDwzMWyH5lRVmK -M0qoTfdjpSg5Jwgd75taWt4bxGJWeflSSv8z5R0BAoGAWL8c527AbeBvx2tOYKkD -2TFranvANNcoMrbeviZSkkGvMNDP3p8b6juJwXOIeWNr8q4vFgCzLmq6d1/9gYm2 -3XJwwyD0LKlqzkBrrKU47qrnmMosUrIRlrAzd3HbShOptxc6Iz2apSaUDKGKXkaw -gl5OpEjeliU7Mus0BVS858g= ------END PRIVATE KEY-----` -}; diff --git a/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestResponseDataParser.ts b/packages/happy-dom/src/xml-http-request/XMLHttpRequestResponseDataParser.ts similarity index 69% rename from packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestResponseDataParser.ts rename to packages/happy-dom/src/xml-http-request/XMLHttpRequestResponseDataParser.ts index 0f780049d..adc2e809e 100644 --- a/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestResponseDataParser.ts +++ b/packages/happy-dom/src/xml-http-request/XMLHttpRequestResponseDataParser.ts @@ -1,8 +1,8 @@ -import XMLHttpResponseTypeEnum from '../XMLHttpResponseTypeEnum.js'; -import XMLHttpRequestResponseTextDecoder from './XMLHttpRequestResponseTextDecoder.js'; -import IBrowserWindow from '../../window/IBrowserWindow.js'; -import Blob from '../../file/Blob.js'; -import IDocument from '../../nodes/document/IDocument.js'; +import XMLHttpResponseTypeEnum from './XMLHttpResponseTypeEnum.js'; +import IBrowserWindow from '../window/IBrowserWindow.js'; +import Blob from '../file/Blob.js'; +import IDocument from '../nodes/document/IDocument.js'; +import { Buffer } from 'buffer'; /** * @@ -48,10 +48,7 @@ export default class XMLHttpRequestResponseDataParser { const domParser = new window.DOMParser(); try { - return domParser.parseFromString( - XMLHttpRequestResponseTextDecoder.decode(options.data, options.contentType), - 'text/xml' - ); + return domParser.parseFromString(options.data.toString(), 'text/xml'); } catch (e) { // Ignore error. } @@ -59,9 +56,7 @@ export default class XMLHttpRequestResponseDataParser { return null; case XMLHttpResponseTypeEnum.json: try { - return JSON.parse( - XMLHttpRequestResponseTextDecoder.decode(options.data, options.contentType) - ); + return JSON.parse(options.data.toString()); } catch (e) { // Ignore error. } @@ -69,7 +64,7 @@ export default class XMLHttpRequestResponseDataParser { case XMLHttpResponseTypeEnum.text: case '': default: - return XMLHttpRequestResponseTextDecoder.decode(options.data, options.contentType); + return options.data.toString(); } } } diff --git a/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestResponseTextDecoder.ts b/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestResponseTextDecoder.ts deleted file mode 100644 index 3fec30c2d..000000000 --- a/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestResponseTextDecoder.ts +++ /dev/null @@ -1,26 +0,0 @@ -import IconvLite from 'iconv-lite'; - -const CONTENT_TYPE_ENCODING_REGEXP = /charset=([^;]*)/i; - -/** - * - */ -export default class XMLHttpRequestResponseTextDecoder { - /** - * Decodes response text. - * - * @param data Data. - * @param [contentType] Content type. - * @returns Decoded text. - **/ - public static decode(data: Buffer, contentType?: string): string { - if (!contentType) { - return IconvLite.decode(data, 'utf-8'); - } - - const contextTypeEncodingRegexp = new RegExp(CONTENT_TYPE_ENCODING_REGEXP, 'gi'); - const charset = contextTypeEncodingRegexp.exec(contentType); - - return IconvLite.decode(data, charset ? charset[1] : 'utf-8'); - } -} diff --git a/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestSyncRequestScriptBuilder.ts b/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestSyncRequestScriptBuilder.ts deleted file mode 100644 index 00fdb1861..000000000 --- a/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestSyncRequestScriptBuilder.ts +++ /dev/null @@ -1,52 +0,0 @@ -import HTTPS from 'https'; - -/** - * Synchroneous XMLHttpRequest script builder. - */ -export default class XMLHttpRequestSyncRequestScriptBuilder { - /** - * Sends a synchronous request. - * - * @param options Options. - * @param ssl SSL. - * @param data Data. - */ - public static getScript(options: HTTPS.RequestOptions, ssl: boolean, data?: string): string { - // Synchronous - // Note: console.log === stdout - // The async request the other Node process executes - return ` - const HTTP = require('http'); - const HTTPS = require('https'); - const sendRequest = HTTP${ssl ? 'S' : ''}.request; - const options = ${JSON.stringify(options)}; - const request = sendRequest(options, (response) => { - let responseText = ''; - let responseData = Buffer.alloc(0); - response.on('data', (chunk) => { - responseText += chunk; - responseData = Buffer.concat([responseData, Buffer.from(chunk)]); - }); - response.on('end', () => { - console.log(JSON.stringify({ - error: null, - data: { - statusCode: response.statusCode, - statusMessage: response.statusMessage, - headers: response.headers, - text: responseText, - data: responseData.toString('base64') - } - })); - }); - response.on('error', (error) => { - console.log(JSON.stringify({ error: error.toString(), data: null })); - }); - }); - request.write(\`${JSON.stringify(data ?? '') - .slice(1, -1) - .replace(/'/g, "\\'")}\`); - request.end(); - `; - } -} diff --git a/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestURLUtility.ts b/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestURLUtility.ts deleted file mode 100644 index c971ebb5f..000000000 --- a/packages/happy-dom/src/xml-http-request/utilities/XMLHttpRequestURLUtility.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { UrlObject } from 'url'; -import URL from '../../url/URL.js'; -import Path from 'path'; - -// MIME type. -const LOCAL_MIME_TYPES = { - json: 'application/json', - xml: 'application/xml', - html: 'text/html', - text: 'text/plain', - txt: 'text/plain', - xhtml: 'application/xhtml+xml', - xht: 'application/xhtml+xml', - xsl: 'application/xml', - xslt: 'application/xml', - rss: 'application/rss+xml', - atom: 'application/atom+xml', - yaml: 'application/x-yaml', - pdf: 'application/pdf', - zip: 'application/zip', - gzip: 'application/gzip', - rar: 'application/x-rar-compressed', - '7z': 'application/x-7z-compressed', - exe: 'application/x-msdownload', - csv: 'text/csv', - ics: 'text/calendar', - rtf: 'text/rtf', - js: 'application/javascript', - css: 'text/css', - apng: 'image/apng', - png: 'image/png', - jpg: 'image/jpeg', - jpeg: 'image/jpeg', - gif: 'image/gif', - bmp: 'image/bmp', - ico: 'image/x-icon', - tiff: 'image/tiff', - tif: 'image/tiff', - svg: 'image/svg+xml', - svgz: 'image/svg+xml', - webp: 'image/webp', - wav: 'audio/wav', - webm: 'video/webm', - mp4: 'video/mp4', - mpeg: 'video/mpeg', - mpg: 'video/mpeg', - mov: 'video/quicktime', - avi: 'video/x-msvideo', - flv: 'video/x-flv', - mkv: 'video/x-matroska', - mka: 'audio/x-matroska', - m3u: 'audio/x-mpegurl', - m3u8: 'application/x-mpegURL', - pls: 'audio/x-scpls', - flac: 'audio/x-flac', - ogg: 'audio/ogg', - oga: 'audio/ogg', - ogv: 'video/ogg', - ogx: 'application/ogg', - opus: 'audio/opus', - spx: 'audio/ogg', - swf: 'application/x-shockwave-flash', - woff: 'font/woff', - woff2: 'font/woff2', - ttf: 'font/ttf', - eot: 'application/vnd.ms-fontobject', - otf: 'font/otf', - sfnt: 'application/font-sfnt', - bin: 'application/octet-stream', - doc: 'application/msword', - docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - xls: 'application/vnd.ms-excel', - xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - ppt: 'application/vnd.ms-powerpoint', - pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - odt: 'application/vnd.oasis.opendocument.text', - ods: 'application/vnd.oasis.opendocument.spreadsheet' -}; - -/** - * URL utility. - */ -export default class XMLHttpRequestURLUtility { - /** - * Returns "true" if SSL. - * - * @param url URL. - * @returns "true" if SSL. - */ - public static isSSL(url: URL): boolean { - return url.protocol === 'https:'; - } - - /** - * Returns "true" if SSL. - * - * @param url URL. - * @returns "true" if SSL. - */ - public static isLocal(url: URL): boolean { - return url.protocol === 'file:'; - } - - /** - * Returns "true" if protocol is valid. - * - * @param url URL. - * @returns "true" if valid. - */ - public static isSupportedProtocol(url: URL): boolean { - switch (url.protocol) { - case 'https:': - case 'http:': - case 'file:': - case undefined: - case '': - return true; - } - - return false; - } - - /** - * Returns host. - * - * @param url URL. - * @returns Host. - */ - public static getHost(url: URL): string { - switch (url.protocol) { - case 'http:': - case 'https:': - return url.hostname; - case undefined: - case '': - return 'localhost'; - default: - return null; - } - } - - /** - * - * @param url - */ - public static getMimeTypeFromExt(url: UrlObject): string { - const extension = Path.extname(url.pathname).replace('.', '').toLowerCase(); - return LOCAL_MIME_TYPES[extension] || 'application/octet-stream'; - } -} diff --git a/packages/happy-dom/test/event/Event.test.ts b/packages/happy-dom/test/event/Event.test.ts index 2b55b5a20..ebb27855b 100644 --- a/packages/happy-dom/test/event/Event.test.ts +++ b/packages/happy-dom/test/event/Event.test.ts @@ -3,7 +3,6 @@ import Window from '../../src/window/Window.js'; import IDocument from '../../src/nodes/document/IDocument.js'; import Event from '../../src/event/Event.js'; import CustomElement from '../CustomElement.js'; -import { performance } from 'perf_hooks'; import { beforeEach, afterEach, describe, it, expect, vi } from 'vitest'; import IEventTarget from '../../src/event/IEventTarget.js'; diff --git a/packages/happy-dom/test/fetch/SyncFetch.test.ts b/packages/happy-dom/test/fetch/SyncFetch.test.ts index e1df2ddfc..09b94a49e 100644 --- a/packages/happy-dom/test/fetch/SyncFetch.test.ts +++ b/packages/happy-dom/test/fetch/SyncFetch.test.ts @@ -2353,7 +2353,7 @@ describe('SyncFetch', () => { 'content-length', String(responseText2.length), 'cache-control', - 'max-age=0.05', + 'max-age=0.5', 'last-modified', 'Mon, 11 Dec 2023 02:00:00 GMT' ], @@ -2439,7 +2439,7 @@ describe('SyncFetch', () => { expect(headers2).toEqual({ 'content-type': 'text/html', 'content-length': String(responseText2.length), - 'cache-control': 'max-age=0.05', + 'cache-control': 'max-age=0.5', 'last-modified': 'Mon, 11 Dec 2023 02:00:00 GMT' }); diff --git a/packages/happy-dom/test/window/BrowserWindow.test.ts b/packages/happy-dom/test/window/BrowserWindow.test.ts index 734d3a4a2..aea633721 100644 --- a/packages/happy-dom/test/window/BrowserWindow.test.ts +++ b/packages/happy-dom/test/window/BrowserWindow.test.ts @@ -144,7 +144,7 @@ describe('BrowserWindow', () => { }); describe('get performance()', () => { - it('Exposes "performance" from the NodeJS perf_hooks package.', () => { + it('Exposes "performance" from NodeJS.', () => { expect(typeof window.performance.now()).toBe('number'); }); }); diff --git a/packages/uncaught-exception-observer/README.md b/packages/uncaught-exception-observer/README.md index 47760d78c..dbb752ec1 100644 --- a/packages/uncaught-exception-observer/README.md +++ b/packages/uncaught-exception-observer/README.md @@ -1,4 +1,4 @@ -:warning: **This package is deprecated. Happy DOM now supports built in by setting "errorCapture" to "processLevel".** :warning: +:warning: **This package has been deprecated. Happy DOM now supports this feature built in by setting "errorCapture" to "processLevel".** :warning: ![Happy DOM Logo](https://github.com/capricorn86/happy-dom/raw/master/docs/happy-dom-logo.jpg) @@ -16,36 +16,36 @@ Uncaught exceptions and rejections must be listened to on the NodeJS process at ### DOM Features -- Custom Elements (Web Components) +- Custom Elements (Web Components) -- Shadow Root (Shadow DOM) +- Shadow Root (Shadow DOM) -- Declarative Shadow DOM +- Declarative Shadow DOM -- Mutation Observer +- Mutation Observer -- Tree Walker +- Tree Walker -- Fetch +- Fetch And much more.. ### Works With -- [Google LitHTML](https://lit-html.polymer-project.org) +- [Google LitHTML](https://lit-html.polymer-project.org) -- [Google LitElement](https://lit-element.polymer-project.org) +- [Google LitElement](https://lit-element.polymer-project.org) -- [React](https://reactjs.org) +- [React](https://reactjs.org) -- [Angular](https://angular.io/) +- [Angular](https://angular.io/) -- [Vue](https://vuejs.org/) +- [Vue](https://vuejs.org/) ### Module Systems -- [ESM](https://nodejs.org/api/esm.html#introduction) -- [CommonJS](https://nodejs.org/api/modules.html#modules-commonjs-modules) +- [ESM](https://nodejs.org/api/esm.html#introduction) +- [CommonJS](https://nodejs.org/api/modules.html#modules-commonjs-modules) # Installation @@ -56,8 +56,8 @@ npm install happy-dom @happy-dom/uncaught-exception-observer # Usage ```javascript -import { Window } from 'happy-dom'; -import { UncaughtExceptionObserver } from '@happy-dom/uncaught-exception-observer'; +import { Window } from "happy-dom"; +import { UncaughtExceptionObserver } from "@happy-dom/uncaught-exception-observer"; const window = new Window(); const document = window.document;