@@ -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) | |||||
) | |||||
}) |