| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152 | 1x
1x
1x
1x
1x
38x
1x
12x
12x
12x
12x
64x
64x
38x
38x
22x
38x
12x
26x
38x
38x
26x
22x
4x
1x
3x
3x
64x
64x
22x
22x
22x
22x
52x
22x
12x
22x
1x
91x
1x
90x
90x
26x
26x
26x
26x
27x
27x
26x
26x
| import {
ParseSourceSpan,
splitNsName,
Element,
Comment,
Text,
Node,
Doctype
} from 'webparser'
let htmlSchema = require('property-information/html')
let svgSchema = require('property-information/svg')
let hastSvg = require('@starptech/prettyhtml-hastscript/svg')
let hast = require('@starptech/prettyhtml-hastscript')
function isFakeRoot(obj: Element): boolean {
return obj.name === ':webparser:root'
}
type Options = {
file?: string
verbose?: boolean
schema?: { space: string }
documentMode?: boolean
}
type HastNode = {
name?: string
type: string
tagName?: string
properties?: Array<Object>
children?: HastNode[]
public?: string
system?: string
value?: string
data?: { [name: string]: any }
}
/* Wrapper to normalise options. */
export = function from(rootNodes: Node[], options: Options) {
const sourceSpan = new ParseSourceSpan(null, null)
const fakeRoot = new Element(':webparser:root', [], rootNodes, sourceSpan)
const result = transform(fakeRoot, {
schema: htmlSchema,
file: options.file,
verbose: options.verbose
})
return result
}
/* Transform a node. */
function transform(ast: Node, config: Options): HastNode {
let schema = config.schema
let node: HastNode
if (ast instanceof Element) {
let children: HastNode[]
config.schema = getNameAndNS(ast.name).ns === 'svg' ? svgSchema : htmlSchema
if (ast.children && ast.children.length) {
children = nodes(ast.children, config)
}
if (isFakeRoot(ast)) {
node = root(ast, children)
} else {
node = element(ast, children, config)
}
node.data = node.data || {}
node.data.selfClosing =
ast.startSourceSpan === ast.endSourceSpan &&
ast.startSourceSpan !== null &&
ast.endSourceSpan !== null
} else if (ast instanceof Text) {
node = text(ast)
} else if (ast instanceof Comment) {
node = comment(ast)
} else Eif (ast instanceof Doctype) {
node = {
type: 'doctype',
name: 'html',
public: null,
system: null
}
}
config.schema = schema
return node
}
/* Transform children. */
function nodes(children: Node[], config: Options): HastNode[] {
let length = children.length
let index = -1
let result: HastNode[] = []
while (++index < length) {
result[index] = transform(children[index], config)
}
return result
}
function root(ast: Node, children: HastNode[]): HastNode {
return { type: 'root', children, data: {} }
}
/* Transform a text. */
function text(ast: Text): HastNode {
return { type: 'text', value: ast.value }
}
/* Transform a comment. */
function comment(ast: Comment): HastNode {
return { type: 'comment', value: ast.value }
}
function getNameAndNS(name: string) {
// support vue :foo attributes but respect
// namepsace syntax from webparser like :ns:attribute
if (name.split(':').length === 2) {
return { ns: null, name: name }
}
const info = splitNsName(name)
return { ns: info[0], name: info[1] }
}
/* Transform an element. */
function element(
ast: Element,
children: HastNode[],
config: Options
): HastNode {
let fn = config.schema.space === 'svg' ? hastSvg : hast
let name = getNameAndNS(ast.name).name
let props: { [name: string]: string } = {}
let node
for (const attr of ast.attrs) {
const attrInfo = getNameAndNS(attr.name)
props[attrInfo.ns ? attrInfo.ns + ':' + attrInfo.name : attrInfo.name] =
attr.value
}
node = fn(name, props, children)
return node
}
|