-
Notifications
You must be signed in to change notification settings - Fork 439
Expand file tree
/
Copy pathhtml.ts
More file actions
104 lines (88 loc) · 3.5 KB
/
html.ts
File metadata and controls
104 lines (88 loc) · 3.5 KB
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
/*
* Copyright (c) 2018, salesforce.com, inc.
* All rights reserved.
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import * as parse5 from 'parse5';
import * as he from 'he';
import { ParserDiagnostics } from '@lwc/errors';
import { APIFeature, isAPIFeatureEnabled } from '@lwc/shared';
import { sourceLocation } from '../shared/ast';
import { errorCodesToErrorOn, errorCodesToWarnOnInOlderAPIVersions } from './parse5Errors';
import { parseFragment } from './expression-complex';
import type { DocumentFragment } from '@parse5/tools';
import type ParserCtx from './parser';
function getLwcErrorFromParse5Error(ctx: ParserCtx, code: string) {
/* istanbul ignore else */
if (errorCodesToErrorOn.has(code)) {
return ParserDiagnostics.INVALID_HTML_SYNTAX;
} else if (errorCodesToWarnOnInOlderAPIVersions.has(code)) {
// In newer API versions, all parse 5 errors are errors, not warnings
if (isAPIFeatureEnabled(APIFeature.TREAT_ALL_PARSE5_ERRORS_AS_ERRORS, ctx.apiVersion)) {
return ParserDiagnostics.INVALID_HTML_SYNTAX;
} else {
return ParserDiagnostics.INVALID_HTML_SYNTAX_WARNING;
}
} else {
// It should be impossible to reach here; we have a test in parser.spec.ts to ensure
// all error codes are accounted for. But just to be safe, make it a warning.
// TODO [#2650]: better system for handling unexpected parse5 errors
// eslint-disable-next-line no-console
console.warn('Found a Parse5 error that we do not know how to handle:', code);
return ParserDiagnostics.INVALID_HTML_SYNTAX_WARNING;
}
}
export function parseHTML(ctx: ParserCtx, source: string): DocumentFragment {
const onParseError = (err: parse5.ParserError) => {
const { code, ...location } = err;
const lwcError = getLwcErrorFromParse5Error(ctx, code);
ctx.warnAtLocation(lwcError, sourceLocation(location), [code]);
};
// TODO [#3370]: remove experimental template expression flag
if (ctx.config.experimentalComplexExpressions) {
return parseFragment(source, {
ctx,
sourceCodeLocationInfo: true,
onParseError,
});
}
return parse5.parseFragment(source, {
sourceCodeLocationInfo: true,
onParseError,
});
}
// https://github.com/babel/babel/blob/d33d02359474296402b1577ef53f20d94e9085c4/packages/babel-types/src/react.js#L9-L55
export function cleanTextNode(value: string): string {
const lines = value.split(/\r\n|\n|\r/);
let lastNonEmptyLine = 0;
for (let i = 0; i < lines.length; i++) {
if (lines[i].match(/[^ \t]/)) {
lastNonEmptyLine = i;
}
}
let str = '';
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const isFirstLine = i === 0;
const isLastLine = i === lines.length - 1;
const isLastNonEmptyLine = i === lastNonEmptyLine;
let trimmedLine = line.replace(/\t/g, ' ');
if (!isFirstLine) {
trimmedLine = trimmedLine.replace(/^[ ]+/, '');
}
if (!isLastLine) {
trimmedLine = trimmedLine.replace(/[ ]+$/, '');
}
if (trimmedLine) {
if (!isLastNonEmptyLine) {
trimmedLine += ' ';
}
str += trimmedLine;
}
}
return str;
}
export function decodeTextContent(source: string): string {
return he.decode(source);
}