-
Notifications
You must be signed in to change notification settings - Fork 44
/
parse.js
109 lines (100 loc) · 3.29 KB
/
parse.js
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
import {
getFormatFromExtension,
getPadding,
getSourcepath,
getTypeFromTag,
getTypeFromType,
isFilepath,
isIgnored,
isRemoteFilepath,
parseAttributes,
parseProps,
} from './utils.js';
import { DefaultHandler, Parser } from 'htmlparser2';
import path from 'node:path';
const RE_COMMENT = /(<!--[^[i][\S\s]+?--\s?>)/gm;
/**
* Parse inlineable sources, modifying passed 'context'
* @param { Context } context
* @returns { Promise<void> }
*/
export async function parse(context) {
// Remove comments
const html = context.html.replace(RE_COMMENT, '');
/** @type { RegExpExecArray | null } */
let match;
// This api uses a synchronous callback handler, so order and definition of 'match' is preserved
const parser = new Parser(
new DefaultHandler((err, dom) => {
if (err) {
throw err;
}
const parsed = /** @type { { attribs: Record<String, string> } } */ (
dom[0]
);
if (parsed) {
const [matching, tag] = /** @type { RegExpExecArray } */ (match);
const attributes = parseAttributes(parsed.attribs);
const props = parseProps(attributes, context.attribute);
const type =
getTypeFromType(/** @type { string } */ (attributes.type)) ||
getTypeFromTag(tag);
const sourcepath = attributes.src || attributes.href || attributes.data;
// Empty sourcepath attribute will be resolved as "true", so skip
// Skip link tags without rel=stylesheet/icon (missing rel assumed to be stylesheet)
if (
sourcepath === true ||
(tag === 'link' &&
attributes.rel &&
attributes.rel !== 'stylesheet' &&
attributes.rel !== 'icon')
) {
return;
}
if (sourcepath === undefined || isFilepath(sourcepath)) {
const filepath = getSourcepath(
/** @type { string } */ (sourcepath),
context.htmlpath,
context.rootpath,
);
const extension = path.extname(filepath[0]).slice(1);
const format = getFormatFromExtension(extension);
// Skip if no source referenced, and ignore based on tag or type
if (!isIgnored(context.ignore, tag, type, format)) {
context.sources.push({
attributes,
compress:
'compress' in props
? /** @type { boolean } */ (props.compress)
: context.compress,
content: null,
errored: false,
extension,
fileContent: '',
filepath: filepath[0],
filepathAnchor: filepath[1],
format,
isRemote: isRemoteFilepath(sourcepath),
match: matching,
padding: context.pretty ? getPadding(matching, context.html) : '',
parentContext: context,
props,
replace: '',
sourcepath,
stack: context.stack,
svgAsImage:
'svgasimage' in props
? /** @type { boolean } */ (props.svgasimage)
: context.svgAsImage,
tag,
type,
});
}
}
}
}),
);
while ((match = context.re.exec(html))) {
parser.parseComplete(match[0]);
}
}