@@ -0,0 +1,2 @@ | |||
node_modules | |||
/lib |
@@ -0,0 +1,28 @@ | |||
# document-outerhtml | |||
Like [Element.outerHTML][], but for the whole [Document][]. | |||
This means it returns a string containing the `<html>.....</html>` with all the content between, plus the `<!DOCTYPE ....>` declaration (if present), and any comments and stray elements or text nodes. | |||
# Install | |||
npm install document-outerhtml | |||
# Usage | |||
import documentOuterHTML from 'document-outerhtml' | |||
const html = documentOuterHTML(document) | |||
# Licence | |||
[CC0](https://creativecommons.org/publicdomain/zero/1.0/); do whatever you want with this code. | |||
# See also | |||
- [XMLSerializer.serializeToString][]; does nearly the same thing, except it creates XML. | |||
[Element.outerHTML]: https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML | |||
[Document]: https://developer.mozilla.org/en-US/docs/Web/API/Document | |||
[XMLSerializer.serializeToString]: https://developer.mozilla.org/en-US/docs/Web/API/XMLSerializer/serializeToString |
@@ -0,0 +1,39 @@ | |||
{ | |||
"name": "document-outerhtml", | |||
"version": "0.0.0", | |||
"description": "Like Element.outerHTML, but for the whole Document.", | |||
"keywords": [ | |||
"dom" | |||
], | |||
"main": "lib", | |||
"scripts": { | |||
"build": "babel -d lib src", | |||
"prepublish": "npm run build", | |||
"test": "ava" | |||
}, | |||
"author": "Gerben <gerben@treora.com>", | |||
"license": "CC0-1.0", | |||
"files": [ | |||
"lib" | |||
], | |||
"devDependencies": { | |||
"ava": "^1.0.0-beta.3", | |||
"babel-cli": "^6.26.0", | |||
"babel-preset-env": "^1.6.1", | |||
"babel-register": "^6.26.0", | |||
"window": "^4.2.5" | |||
}, | |||
"babel": { | |||
"presets": [ | |||
"env" | |||
] | |||
}, | |||
"ava": { | |||
"require": [ | |||
"babel-register" | |||
] | |||
}, | |||
"dependencies": { | |||
"doctype-to-string": "^0.1.1" | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
import doctypeToString from 'doctype-to-string' | |||
export default function documentOuterHTML(document) { | |||
if (!document | |||
|| document.nodeType === undefined | |||
|| document.nodeType !== document.DOCUMENT_NODE | |||
) { | |||
throw new TypeError('Expected a Document') | |||
} | |||
const html = [...document.childNodes] | |||
.map(node => nodeToString(node)) | |||
.join('\n') | |||
return html | |||
} | |||
function nodeToString(node) { | |||
switch (node.nodeType) { | |||
case node.ELEMENT_NODE: | |||
return node.outerHTML | |||
case node.TEXT_NODE: | |||
return node.textContent | |||
case node.COMMENT_NODE: | |||
return `<!--${node.textContent}-->` | |||
case node.DOCUMENT_TYPE_NODE: | |||
return doctypeToString(node) | |||
default: | |||
throw new TypeError(`Unexpected node type: ${node.nodeType}`) | |||
} | |||
} |
@@ -0,0 +1,89 @@ | |||
import test from 'ava' | |||
import Window from 'window'; | |||
import documentOuterHTML from '../src' | |||
function makeDocument(html) { | |||
const window = new Window() | |||
const parser = new window.DOMParser() | |||
const doc = parser.parseFromString( | |||
html, | |||
'text/html' | |||
) | |||
return doc | |||
} | |||
function normalise(html) { | |||
// Remove whitespace around string and between tags. | |||
return html.trim().replace(/>\s+</g, '><') | |||
} | |||
test('should work with only an html node', t => { | |||
const html = ` | |||
<html> | |||
<head></head> | |||
<body><p>blub.</p></body> | |||
</html> | |||
` | |||
const doc = makeDocument(html) | |||
t.is( | |||
normalise(documentOuterHTML(doc)), | |||
normalise(html) | |||
) | |||
}) | |||
test('should work with comments', t => { | |||
const html = ` | |||
<!-- comment before --> | |||
<html> | |||
<head></head> | |||
<body><p>blub.</p></body> | |||
</html> | |||
<!-- comment after --> | |||
` | |||
const doc = makeDocument(html) | |||
t.is( | |||
normalise(documentOuterHTML(doc)), | |||
normalise(html) | |||
) | |||
}) | |||
test('should work with doctype', t => { | |||
const html = ` | |||
<!DOCTYPE html> | |||
<html> | |||
<head></head> | |||
<body><p>blub.</p></body> | |||
</html> | |||
` | |||
const doc = makeDocument(html) | |||
t.is( | |||
normalise(documentOuterHTML(doc)), | |||
normalise(html) | |||
) | |||
}) | |||
test('should work without a documentElement', t => { | |||
const html = ` | |||
<!DOCTYPE html> | |||
<!-- some comment --> | |||
` | |||
const doc = makeDocument(html) | |||
// The html (+head&body) element will have been created automatically. Remove it. | |||
doc.removeChild(doc.documentElement) | |||
t.is( | |||
normalise(documentOuterHTML(doc)), | |||
normalise(html) | |||
) | |||
}) | |||
test('should even work on a completely empty document', t => { | |||
const html = `` | |||
const doc = makeDocument(html) | |||
// The html (+head&body) element will have been created automatically. Remove it. | |||
doc.removeChild(doc.documentElement) | |||
t.is( | |||
normalise(documentOuterHTML(doc)), | |||
normalise(html) | |||
) | |||
}) |