Files

289 lines
7.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict'
var Parser = require('parse5/lib/parser')
var pos = require('unist-util-position')
var fromParse5 = require('hast-util-from-parse5')
var toParse5 = require('hast-util-to-parse5')
var voids = require('html-void-elements')
var ns = require('web-namespaces')
var zwitch = require('zwitch')
var xtend = require('xtend')
module.exports = wrap
var inTemplateMode = 'IN_TEMPLATE_MODE'
var dataState = 'DATA_STATE'
var characterToken = 'CHARACTER_TOKEN'
var startTagToken = 'START_TAG_TOKEN'
var endTagToken = 'END_TAG_TOKEN'
var commentToken = 'COMMENT_TOKEN'
var doctypeToken = 'DOCTYPE_TOKEN'
var parseOptions = {
sourceCodeLocationInfo: true,
scriptingEnabled: false
}
function wrap(tree, file) {
var parser = new Parser(parseOptions)
var one = zwitch('type')
var tokenizer
var preprocessor
var posTracker
var locationTracker
var result
one.handlers.root = root
one.handlers.element = element
one.handlers.text = text
one.handlers.comment = comment
one.handlers.doctype = doctype
one.handlers.raw = raw
one.unknown = unknown
result = fromParse5(documentMode(tree) ? document() : fragment(), file)
// Unpack if possible and when not given a `root`.
if (tree.type !== 'root' && result.children.length === 1) {
return result.children[0]
}
return result
function fragment() {
var context
var mock
var doc
context = {
nodeName: 'template',
tagName: 'template',
attrs: [],
namespaceURI: ns.html,
childNodes: []
}
mock = {
nodeName: 'documentmock',
tagName: 'documentmock',
attrs: [],
namespaceURI: ns.html,
childNodes: []
}
doc = {
nodeName: '#document-fragment',
childNodes: []
}
parser._bootstrap(mock, context)
parser._pushTmplInsertionMode(inTemplateMode)
parser._initTokenizerForFragmentParsing()
parser._insertFakeRootElement()
parser._resetInsertionMode()
parser._findFormInFragmentContext()
tokenizer = parser.tokenizer
preprocessor = tokenizer.preprocessor
locationTracker = tokenizer.__mixins[0]
posTracker = locationTracker.posTracker
one(tree)
parser._adoptNodes(mock.childNodes[0], doc)
return doc
}
function document() {
var doc = parser.treeAdapter.createDocument()
parser._bootstrap(doc, null)
tokenizer = parser.tokenizer
preprocessor = tokenizer.preprocessor
locationTracker = tokenizer.__mixins[0]
posTracker = locationTracker.posTracker
one(tree)
return doc
}
function all(nodes) {
var length = 0
var index = -1
/* istanbul ignore else - invalid nodes, see rehypejs/rehype-raw#7. */
if (nodes) {
length = nodes.length
}
while (++index < length) {
one(nodes[index])
}
}
function root(node) {
all(node.children)
}
function element(node) {
var empty = voids.indexOf(node.tagName) !== -1
parser._processToken(startTag(node), ns.html)
all(node.children)
if (!empty) {
parser._processToken(endTag(node))
// Put the parser back in the data state: some elements, like textareas
// and iframes, change the state.
// See <syntax-tree/hast-util-raw#7>.
// See <https://github.com/inikulin/parse5/blob/2528196/packages/parse5/lib/tokenizer/index.js#L222>.
tokenizer.state = dataState
}
}
function text(node) {
parser._processToken({
type: characterToken,
chars: node.value,
location: createParse5Location(node)
})
}
function doctype(node) {
var p5 = toParse5(node)
parser._processToken({
type: doctypeToken,
name: p5.name,
forceQuirks: false,
publicId: p5.publicId,
systemId: p5.systemId,
location: createParse5Location(node)
})
}
function comment(node) {
parser._processToken({
type: commentToken,
data: node.value,
location: createParse5Location(node)
})
}
function raw(node) {
var start = pos.start(node)
var line = start.line || 1
var column = start.column || 1
var offset = start.offset || 0
var token
// Reset preprocessor:
// See: <https://github.com/inikulin/parse5/blob/0491902/packages/parse5/lib/tokenizer/preprocessor.js>.
preprocessor.html = null
preprocessor.endOfChunkHit = false
preprocessor.lastChunkWritten = false
preprocessor.lastCharPos = -1
preprocessor.pos = -1
// Reset preprocessor mixin:
// See: <https://github.com/inikulin/parse5/blob/0491902/packages/parse5/lib/extensions/position-tracking/preprocessor-mixin.js>.
posTracker.droppedBufferSize = 0
posTracker.line = line
posTracker.col = 1
posTracker.offset = 0
posTracker.lineStartPos = -column + 1
posTracker.droppedBufferSize = offset
// Reset location tracker:
// See: <https://github.com/inikulin/parse5/blob/0491902/packages/parse5/lib/extensions/location-info/tokenizer-mixin.js>.
locationTracker.currentAttrLocation = null
locationTracker.ctLoc = createParse5Location(node)
// See the code for `parse` and `parseFragment`:
// See: <https://github.com/inikulin/parse5/blob/0491902/packages/parse5/lib/parser/index.js#L371>.
tokenizer.write(node.value)
parser._runParsingLoop(null)
// Process final characters if theyre still there after hibernating.
// Similar to:
// See: <https://github.com/inikulin/parse5/blob/3bfa7d9/packages/parse5/lib/extensions/location-info/tokenizer-mixin.js#L95>.
token = tokenizer.currentCharacterToken
if (token) {
token.location.endLine = posTracker.line
token.location.endCol = posTracker.col + 1
token.location.endOffset = posTracker.offset + 1
parser._processToken(token)
}
// Reset tokenizer:
// See: <https://github.com/inikulin/parse5/blob/8b0048e/packages/parse5/lib/tokenizer/index.js#L215>.
tokenizer.currentToken = null
tokenizer.currentCharacterToken = null
tokenizer.currentAttr = null
}
}
function startTag(node) {
var location = createParse5Location(node)
location.startTag = xtend(location)
return {
type: startTagToken,
tagName: node.tagName,
selfClosing: false,
attrs: attributes(node),
location: location
}
}
function attributes(node) {
return toParse5({
tagName: node.tagName,
type: 'element',
properties: node.properties
}).attrs
}
function endTag(node) {
var location = createParse5Location(node)
location.endTag = xtend(location)
return {
type: endTagToken,
tagName: node.tagName,
attrs: [],
location: location
}
}
function unknown(node) {
throw new Error('Cannot compile `' + node.type + '` node')
}
function documentMode(node) {
var head = node.type === 'root' ? node.children[0] : node
return head && (head.type === 'doctype' || head.tagName === 'html')
}
function createParse5Location(node) {
var start = pos.start(node)
var end = pos.end(node)
return {
startLine: start.line,
startCol: start.column,
startOffset: start.offset,
endLine: end.line,
endCol: end.column,
endOffset: end.offset
}
}