@lwc/template-compiler 2.45.2 → 2.45.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.cjs.js +5313 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/{types/index.d.ts → index.d.ts} +1 -0
- package/dist/index.js +5286 -0
- package/dist/index.js.map +1 -0
- package/package.json +33 -30
- package/dist/commonjs/codegen/codegen.js +0 -330
- package/dist/commonjs/codegen/codegen.js.map +0 -1
- package/dist/commonjs/codegen/expression.js +0 -176
- package/dist/commonjs/codegen/expression.js.map +0 -1
- package/dist/commonjs/codegen/formatters/module.js +0 -91
- package/dist/commonjs/codegen/formatters/module.js.map +0 -1
- package/dist/commonjs/codegen/helpers.js +0 -233
- package/dist/commonjs/codegen/helpers.js.map +0 -1
- package/dist/commonjs/codegen/index.js +0 -549
- package/dist/commonjs/codegen/index.js.map +0 -1
- package/dist/commonjs/codegen/optimize.js +0 -119
- package/dist/commonjs/codegen/optimize.js.map +0 -1
- package/dist/commonjs/codegen/static-element-serializer.js +0 -122
- package/dist/commonjs/codegen/static-element-serializer.js.map +0 -1
- package/dist/commonjs/config.js +0 -78
- package/dist/commonjs/config.js.map +0 -1
- package/dist/commonjs/index.js +0 -66
- package/dist/commonjs/index.js.map +0 -1
- package/dist/commonjs/parser/attribute.js +0 -237
- package/dist/commonjs/parser/attribute.js.map +0 -1
- package/dist/commonjs/parser/constants.js +0 -155
- package/dist/commonjs/parser/constants.js.map +0 -1
- package/dist/commonjs/parser/expression-complex/html.js +0 -222
- package/dist/commonjs/parser/expression-complex/html.js.map +0 -1
- package/dist/commonjs/parser/expression-complex/index.js +0 -26
- package/dist/commonjs/parser/expression-complex/index.js.map +0 -1
- package/dist/commonjs/parser/expression-complex/types.js +0 -9
- package/dist/commonjs/parser/expression-complex/types.js.map +0 -1
- package/dist/commonjs/parser/expression-complex/validate.js +0 -141
- package/dist/commonjs/parser/expression-complex/validate.js.map +0 -1
- package/dist/commonjs/parser/expression.js +0 -130
- package/dist/commonjs/parser/expression.js.map +0 -1
- package/dist/commonjs/parser/html.js +0 -112
- package/dist/commonjs/parser/html.js.map +0 -1
- package/dist/commonjs/parser/index.js +0 -1181
- package/dist/commonjs/parser/index.js.map +0 -1
- package/dist/commonjs/parser/parse5Errors.js +0 -77
- package/dist/commonjs/parser/parse5Errors.js.map +0 -1
- package/dist/commonjs/parser/parser.js +0 -309
- package/dist/commonjs/parser/parser.js.map +0 -1
- package/dist/commonjs/parser/utils/html-element-attributes.js +0 -411
- package/dist/commonjs/parser/utils/html-element-attributes.js.map +0 -1
- package/dist/commonjs/parser/utils/html-elements.js +0 -141
- package/dist/commonjs/parser/utils/html-elements.js.map +0 -1
- package/dist/commonjs/parser/utils/javascript.js +0 -25
- package/dist/commonjs/parser/utils/javascript.js.map +0 -1
- package/dist/commonjs/parser/utils/svg-elements.js +0 -42
- package/dist/commonjs/parser/utils/svg-elements.js.map +0 -1
- package/dist/commonjs/shared/ast.js +0 -496
- package/dist/commonjs/shared/ast.js.map +0 -1
- package/dist/commonjs/shared/constants.js +0 -33
- package/dist/commonjs/shared/constants.js.map +0 -1
- package/dist/commonjs/shared/estree.js +0 -325
- package/dist/commonjs/shared/estree.js.map +0 -1
- package/dist/commonjs/shared/naming.js +0 -32
- package/dist/commonjs/shared/naming.js.map +0 -1
- package/dist/commonjs/shared/parse5.js +0 -20
- package/dist/commonjs/shared/parse5.js.map +0 -1
- package/dist/commonjs/shared/renderer-hooks.js +0 -55
- package/dist/commonjs/shared/renderer-hooks.js.map +0 -1
- package/dist/commonjs/shared/types.js +0 -49
- package/dist/commonjs/shared/types.js.map +0 -1
- package/dist/commonjs/shared/utils.js +0 -45
- package/dist/commonjs/shared/utils.js.map +0 -1
- package/dist/commonjs/state.js +0 -24
- package/dist/commonjs/state.js.map +0 -1
- /package/dist/{types/codegen → codegen}/codegen.d.ts +0 -0
- /package/dist/{types/codegen → codegen}/expression.d.ts +0 -0
- /package/dist/{types/codegen → codegen}/formatters/module.d.ts +0 -0
- /package/dist/{types/codegen → codegen}/helpers.d.ts +0 -0
- /package/dist/{types/codegen → codegen}/index.d.ts +0 -0
- /package/dist/{types/codegen → codegen}/optimize.d.ts +0 -0
- /package/dist/{types/codegen → codegen}/static-element-serializer.d.ts +0 -0
- /package/dist/{types/config.d.ts → config.d.ts} +0 -0
- /package/dist/{types/parser → parser}/attribute.d.ts +0 -0
- /package/dist/{types/parser → parser}/constants.d.ts +0 -0
- /package/dist/{types/parser → parser}/expression-complex/html.d.ts +0 -0
- /package/dist/{types/parser → parser}/expression-complex/index.d.ts +0 -0
- /package/dist/{types/parser → parser}/expression-complex/types.d.ts +0 -0
- /package/dist/{types/parser → parser}/expression-complex/validate.d.ts +0 -0
- /package/dist/{types/parser → parser}/expression.d.ts +0 -0
- /package/dist/{types/parser → parser}/html.d.ts +0 -0
- /package/dist/{types/parser → parser}/index.d.ts +0 -0
- /package/dist/{types/parser → parser}/parse5Errors.d.ts +0 -0
- /package/dist/{types/parser → parser}/parser.d.ts +0 -0
- /package/dist/{types/parser → parser}/utils/html-element-attributes.d.ts +0 -0
- /package/dist/{types/parser → parser}/utils/html-elements.d.ts +0 -0
- /package/dist/{types/parser → parser}/utils/javascript.d.ts +0 -0
- /package/dist/{types/parser → parser}/utils/svg-elements.d.ts +0 -0
- /package/dist/{types/shared → shared}/ast.d.ts +0 -0
- /package/dist/{types/shared → shared}/constants.d.ts +0 -0
- /package/dist/{types/shared → shared}/estree.d.ts +0 -0
- /package/dist/{types/shared → shared}/naming.d.ts +0 -0
- /package/dist/{types/shared → shared}/parse5.d.ts +0 -0
- /package/dist/{types/shared → shared}/renderer-hooks.d.ts +0 -0
- /package/dist/{types/shared → shared}/types.d.ts +0 -0
- /package/dist/{types/shared → shared}/utils.d.ts +0 -0
- /package/dist/{types/state.d.ts → state.d.ts} +0 -0
|
@@ -1,1181 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
const shared_1 = require("@lwc/shared");
|
|
30
|
-
const errors_1 = require("@lwc/errors");
|
|
31
|
-
const t = __importStar(require("../shared/estree"));
|
|
32
|
-
const parse5Utils = __importStar(require("../shared/parse5"));
|
|
33
|
-
const ast = __importStar(require("../shared/ast"));
|
|
34
|
-
const types_1 = require("../shared/types");
|
|
35
|
-
const utils_1 = require("../shared/utils");
|
|
36
|
-
const constants_1 = require("../shared/constants");
|
|
37
|
-
const parser_1 = __importDefault(require("./parser"));
|
|
38
|
-
const html_1 = require("./html");
|
|
39
|
-
const expression_1 = require("./expression");
|
|
40
|
-
const attribute_1 = require("./attribute");
|
|
41
|
-
const constants_2 = require("./constants");
|
|
42
|
-
function attributeExpressionReferencesForOfIndex(attribute, forOf) {
|
|
43
|
-
const { value } = attribute;
|
|
44
|
-
// if not an expression, it is not referencing iterator index
|
|
45
|
-
if (!t.isMemberExpression(value)) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
const { object, property } = value;
|
|
49
|
-
if (!t.isIdentifier(object) || !t.isIdentifier(property)) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
if (forOf.iterator.name !== object.name) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
return property.name === 'index';
|
|
56
|
-
}
|
|
57
|
-
function attributeExpressionReferencesForEachIndex(attribute, forEach) {
|
|
58
|
-
const { index } = forEach;
|
|
59
|
-
const { value } = attribute;
|
|
60
|
-
// No index defined on foreach
|
|
61
|
-
if (!index || !t.isIdentifier(index) || !t.isIdentifier(value)) {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
return index.name === value.name;
|
|
65
|
-
}
|
|
66
|
-
function parse(source, state) {
|
|
67
|
-
const ctx = new parser_1.default(source, state.config);
|
|
68
|
-
const fragment = (0, html_1.parseHTML)(ctx, source);
|
|
69
|
-
if (ctx.warnings.some((_) => _.level === errors_1.DiagnosticLevel.Error)) {
|
|
70
|
-
return { warnings: ctx.warnings };
|
|
71
|
-
}
|
|
72
|
-
const root = ctx.withErrorRecovery(() => {
|
|
73
|
-
const templateRoot = getTemplateRoot(ctx, fragment);
|
|
74
|
-
return parseRoot(ctx, templateRoot);
|
|
75
|
-
});
|
|
76
|
-
return { root, warnings: ctx.warnings };
|
|
77
|
-
}
|
|
78
|
-
exports.default = parse;
|
|
79
|
-
function parseRoot(ctx, parse5Elm) {
|
|
80
|
-
const { sourceCodeLocation: rootLocation } = parse5Elm;
|
|
81
|
-
/* istanbul ignore if */
|
|
82
|
-
if (!rootLocation) {
|
|
83
|
-
// Parse5 will recover from invalid HTML. When this happens the node's sourceCodeLocation will be undefined.
|
|
84
|
-
// https://github.com/inikulin/parse5/blob/master/packages/parse5/docs/options/parser-options.md#sourcecodelocationinfo
|
|
85
|
-
// This is a defensive check as this should never happen for the root template.
|
|
86
|
-
throw new Error('An internal parsing error occurred during node creation; the root template node does not have a sourceCodeLocation.');
|
|
87
|
-
}
|
|
88
|
-
if (parse5Elm.tagName !== 'template') {
|
|
89
|
-
ctx.throw(errors_1.ParserDiagnostics.ROOT_TAG_SHOULD_BE_TEMPLATE, [parse5Elm.tagName], ast.sourceLocation(rootLocation));
|
|
90
|
-
}
|
|
91
|
-
const parsedAttr = parseAttributes(ctx, parse5Elm, rootLocation);
|
|
92
|
-
const root = ast.root(rootLocation);
|
|
93
|
-
applyRootLwcDirectives(ctx, parsedAttr, root);
|
|
94
|
-
ctx.setRootDirective(root);
|
|
95
|
-
validateRoot(ctx, parsedAttr, root);
|
|
96
|
-
parseChildren(ctx, parse5Elm, root, rootLocation);
|
|
97
|
-
return root;
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* This function will create LWC AST nodes from an HTML element.
|
|
101
|
-
* A node is generated for each LWC HTML template directive attached to the
|
|
102
|
-
* element as well as the element itself (excluding template tag elements).
|
|
103
|
-
*
|
|
104
|
-
* The hierarchy of nodes created is as follows:
|
|
105
|
-
*
|
|
106
|
-
* For/Iterator -> If -> Element/Component/Slot
|
|
107
|
-
*
|
|
108
|
-
* For each node that's created, the parent will be the most recently
|
|
109
|
-
* created node otherwise it will be parentNode.
|
|
110
|
-
*
|
|
111
|
-
* Note: Not every node in the hierarchy is guaranteed to be created, for example,
|
|
112
|
-
* <div></div> will only create an Element node.
|
|
113
|
-
*/
|
|
114
|
-
function parseElement(ctx, parse5Elm, parentNode, parse5ParentLocation) {
|
|
115
|
-
const parse5ElmLocation = parseElementLocation(ctx, parse5Elm, parse5ParentLocation);
|
|
116
|
-
const parsedAttr = parseAttributes(ctx, parse5Elm, parse5ElmLocation);
|
|
117
|
-
// Create an AST node for each LWC template directive and chain them into a parent child hierarchy
|
|
118
|
-
const directive = parseElementDirectives(ctx, parse5Elm, parse5ElmLocation, parentNode, parsedAttr);
|
|
119
|
-
// Create an AST node for the HTML element (excluding template tag elements) and add as child to parent
|
|
120
|
-
const element = parseBaseElement(ctx, parsedAttr, parse5Elm, directive !== null && directive !== void 0 ? directive : parentNode, parse5ElmLocation);
|
|
121
|
-
if (element) {
|
|
122
|
-
applyHandlers(ctx, parsedAttr, element);
|
|
123
|
-
applyKey(ctx, parsedAttr, element);
|
|
124
|
-
applyLwcDirectives(ctx, parsedAttr, element);
|
|
125
|
-
applyAttributes(ctx, parsedAttr, element);
|
|
126
|
-
validateElement(ctx, element, parse5Elm);
|
|
127
|
-
validateAttributes(ctx, parsedAttr, element);
|
|
128
|
-
validateProperties(ctx, element);
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
// parseBaseElement will always return an element EXCEPT when processing a <template>
|
|
132
|
-
validateTemplate(ctx, parsedAttr, parse5Elm, parse5ElmLocation);
|
|
133
|
-
}
|
|
134
|
-
const currentNode = element !== null && element !== void 0 ? element : directive;
|
|
135
|
-
if (currentNode) {
|
|
136
|
-
parseChildren(ctx, parse5Elm, currentNode, parse5ElmLocation);
|
|
137
|
-
validateChildren(ctx, element, directive);
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
// The only scenario where currentNode can be undefined is when there are only invalid attributes on a template element.
|
|
141
|
-
// For example, <template class='slds-hello-world'>, these template elements and their children will not be rendered.
|
|
142
|
-
ctx.warnAtLocation(errors_1.ParserDiagnostics.INVALID_TEMPLATE_WARNING, ast.sourceLocation(parse5ElmLocation));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
function parseElementLocation(ctx, parse5Elm, parse5ParentLocation) {
|
|
146
|
-
var _a;
|
|
147
|
-
let location = parse5Elm.sourceCodeLocation;
|
|
148
|
-
// AST hierarchy is ForBlock > If > BaseElement, if immediate parent is not a BaseElement it is a template.
|
|
149
|
-
const parentNode = ctx.findAncestor(ast.isBaseElement, () => false);
|
|
150
|
-
if (!location) {
|
|
151
|
-
// Parse5 will recover from invalid HTML. When this happens the element's sourceCodeLocation will be undefined.
|
|
152
|
-
// https://github.com/inikulin/parse5/blob/master/packages/parse5/docs/options/parser-options.md#sourcecodelocationinfo
|
|
153
|
-
ctx.warn(errors_1.ParserDiagnostics.INVALID_HTML_RECOVERY, [
|
|
154
|
-
parse5Elm.tagName,
|
|
155
|
-
(_a = parentNode === null || parentNode === void 0 ? void 0 : parentNode.name) !== null && _a !== void 0 ? _a : 'template',
|
|
156
|
-
]);
|
|
157
|
-
}
|
|
158
|
-
// With parse5 automatically recovering from invalid HTML, some AST nodes might not have
|
|
159
|
-
// location information. For example when a <table> element has a <tr> child element, parse5
|
|
160
|
-
// creates a <tbody> element in the middle without location information. In this case, we
|
|
161
|
-
// can safely skip the closing tag validation.
|
|
162
|
-
let current = parse5Elm;
|
|
163
|
-
while (!location && parse5Utils.isElementNode(current.parentNode)) {
|
|
164
|
-
current = current.parentNode;
|
|
165
|
-
location = current.sourceCodeLocation;
|
|
166
|
-
}
|
|
167
|
-
return location !== null && location !== void 0 ? location : parse5ParentLocation;
|
|
168
|
-
}
|
|
169
|
-
const DIRECTIVE_PARSERS = [
|
|
170
|
-
parseIfBlock,
|
|
171
|
-
parseElseifBlock,
|
|
172
|
-
parseElseBlock,
|
|
173
|
-
parseForEach,
|
|
174
|
-
parseForOf,
|
|
175
|
-
parseIf,
|
|
176
|
-
parseScopedSlotFragment,
|
|
177
|
-
];
|
|
178
|
-
function parseElementDirectives(ctx, parse5Elm, parse5ElmLocation, parent, parsedAttr) {
|
|
179
|
-
let current;
|
|
180
|
-
for (const parser of DIRECTIVE_PARSERS) {
|
|
181
|
-
const prev = current || parent;
|
|
182
|
-
const node = parser(ctx, parse5Elm, parse5ElmLocation, prev, parsedAttr);
|
|
183
|
-
if (node) {
|
|
184
|
-
current = node;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return current;
|
|
188
|
-
}
|
|
189
|
-
function parseBaseElement(ctx, parsedAttr, parse5Elm, parent, parse5ElmLocation) {
|
|
190
|
-
const { tagName: tag, namespaceURI } = parse5Elm;
|
|
191
|
-
let element;
|
|
192
|
-
if (tag === 'slot') {
|
|
193
|
-
element = parseSlot(ctx, parsedAttr, parse5ElmLocation);
|
|
194
|
-
// Skip creating template nodes
|
|
195
|
-
}
|
|
196
|
-
else if (tag !== 'template') {
|
|
197
|
-
// Check if the element tag is a valid custom element name and is not part of known standard
|
|
198
|
-
// element name containing a dash.
|
|
199
|
-
if ((0, utils_1.isCustomElementTag)(tag)) {
|
|
200
|
-
if (parsedAttr.get(types_1.ElementDirectiveName.External)) {
|
|
201
|
-
element = ast.externalComponent(tag, parse5ElmLocation);
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
element = ast.component(tag, parse5ElmLocation);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
else if ((0, utils_1.isLwcElementTag)(tag)) {
|
|
208
|
-
// Special tag names that begin with lwc:*
|
|
209
|
-
element = parseLwcElement(ctx, parse5Elm, parsedAttr, parse5ElmLocation);
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
// Built-in HTML elements
|
|
213
|
-
element = ast.element(tag, namespaceURI, parse5ElmLocation);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (element) {
|
|
217
|
-
ctx.addNodeCurrentElementScope(element);
|
|
218
|
-
parent.children.push(element);
|
|
219
|
-
}
|
|
220
|
-
return element;
|
|
221
|
-
}
|
|
222
|
-
function parseLwcElement(ctx, parse5Elm, parsedAttr, parse5ElmLocation) {
|
|
223
|
-
let lwcElementParser;
|
|
224
|
-
switch (parse5Elm.tagName) {
|
|
225
|
-
case types_1.LwcTagName.Component:
|
|
226
|
-
lwcElementParser = parseLwcComponent;
|
|
227
|
-
break;
|
|
228
|
-
default:
|
|
229
|
-
lwcElementParser = parseLwcElementAsBuiltIn;
|
|
230
|
-
}
|
|
231
|
-
return lwcElementParser(ctx, parse5Elm, parsedAttr, parse5ElmLocation);
|
|
232
|
-
}
|
|
233
|
-
function parseLwcComponent(ctx, parse5Elm, parsedAttr, parse5ElmLocation) {
|
|
234
|
-
if (!ctx.config.enableDynamicComponents) {
|
|
235
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.INVALID_OPTS_LWC_ENABLE_DYNAMIC_COMPONENTS, ast.sourceLocation(parse5ElmLocation));
|
|
236
|
-
}
|
|
237
|
-
// <lwc:component> must be used with lwc:is directive
|
|
238
|
-
if (!parsedAttr.get(types_1.ElementDirectiveName.Is)) {
|
|
239
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_COMPONENT_TAG_WITHOUT_IS_DIRECTIVE, ast.sourceLocation(parse5ElmLocation));
|
|
240
|
-
}
|
|
241
|
-
return ast.lwcComponent(parse5Elm.tagName, parse5ElmLocation);
|
|
242
|
-
}
|
|
243
|
-
function parseLwcElementAsBuiltIn(ctx, parse5Elm, _parsedAttr, parse5ElmLocation) {
|
|
244
|
-
const { tagName: tag, namespaceURI } = parse5Elm;
|
|
245
|
-
// Certain tag names that start with lwc:* are signals to the compiler for special behavior.
|
|
246
|
-
// These tag names are listed in LwcTagNames in types.ts.
|
|
247
|
-
// Issue a warning when component authors use an unrecognized lwc:* tag.
|
|
248
|
-
ctx.warnAtLocation(errors_1.ParserDiagnostics.UNSUPPORTED_LWC_TAG_NAME, ast.sourceLocation(parse5ElmLocation), [tag]);
|
|
249
|
-
return ast.element(tag, namespaceURI, parse5ElmLocation);
|
|
250
|
-
}
|
|
251
|
-
function parseChildren(ctx, parse5Parent, parent, parse5ParentLocation) {
|
|
252
|
-
var _a;
|
|
253
|
-
const children = ((_a = parse5Utils.getTemplateContent(parse5Parent)) !== null && _a !== void 0 ? _a : parse5Parent).childNodes;
|
|
254
|
-
ctx.beginSiblingScope();
|
|
255
|
-
for (const child of children) {
|
|
256
|
-
ctx.withErrorRecovery(() => {
|
|
257
|
-
if (parse5Utils.isElementNode(child)) {
|
|
258
|
-
ctx.beginElementScope();
|
|
259
|
-
parseElement(ctx, child, parent, parse5ParentLocation);
|
|
260
|
-
// If we're parsing a chain of if/elseif/else nodes, any node other than
|
|
261
|
-
// an else-if node ends the chain.
|
|
262
|
-
const node = ctx.endElementScope();
|
|
263
|
-
if (node &&
|
|
264
|
-
ctx.isParsingSiblingIfBlock() &&
|
|
265
|
-
!ast.isIfBlock(node) &&
|
|
266
|
-
!ast.isElseifBlock(node)) {
|
|
267
|
-
ctx.endIfChain();
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
else if (parse5Utils.isTextNode(child)) {
|
|
271
|
-
const textNodes = parseText(ctx, child);
|
|
272
|
-
parent.children.push(...textNodes);
|
|
273
|
-
// Non whitespace text nodes end any if chain we may be parsing
|
|
274
|
-
if (ctx.isParsingSiblingIfBlock() && textNodes.length > 0) {
|
|
275
|
-
ctx.endIfChain();
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
else if (parse5Utils.isCommentNode(child)) {
|
|
279
|
-
const commentNode = parseComment(child);
|
|
280
|
-
parent.children.push(commentNode);
|
|
281
|
-
// If preserveComments is enabled, comments become syntactically meaningful and
|
|
282
|
-
// end any if chain we may be parsing
|
|
283
|
-
if (ctx.isParsingSiblingIfBlock() && ctx.preserveComments) {
|
|
284
|
-
ctx.endIfChain();
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
ctx.endSiblingScope();
|
|
290
|
-
}
|
|
291
|
-
function parseText(ctx, parse5Text) {
|
|
292
|
-
const parsedTextNodes = [];
|
|
293
|
-
const location = parse5Text.sourceCodeLocation;
|
|
294
|
-
/* istanbul ignore if */
|
|
295
|
-
if (!location) {
|
|
296
|
-
// Parse5 will recover from invalid HTML. When this happens the node's sourceCodeLocation will be undefined.
|
|
297
|
-
// https://github.com/inikulin/parse5/blob/master/packages/parse5/docs/options/parser-options.md#sourcecodelocationinfo
|
|
298
|
-
// This is a defensive check as this should never happen for TextNode.
|
|
299
|
-
throw new Error('An internal parsing error occurred during node creation; a text node was found without a sourceCodeLocation.');
|
|
300
|
-
}
|
|
301
|
-
// Extract the raw source to avoid HTML entity decoding done by parse5
|
|
302
|
-
const rawText = (0, html_1.cleanTextNode)(ctx.getSource(location.startOffset, location.endOffset));
|
|
303
|
-
if (!rawText.trim().length) {
|
|
304
|
-
return parsedTextNodes;
|
|
305
|
-
}
|
|
306
|
-
// TODO [#3370]: remove experimental template expression flag
|
|
307
|
-
if (ctx.config.experimentalComplexExpressions && (0, expression_1.isExpression)(rawText)) {
|
|
308
|
-
// Implementation of the lexer ensures that each text-node template expression
|
|
309
|
-
// will be contained in its own text node. Adjacent static text will be in
|
|
310
|
-
// separate text nodes.
|
|
311
|
-
const preparsedExpression = ctx.preparsedJsExpressions.get(location.startOffset);
|
|
312
|
-
if (!preparsedExpression) {
|
|
313
|
-
throw new Error('Implementation error: cannot find preparsed template expression');
|
|
314
|
-
}
|
|
315
|
-
const value = {
|
|
316
|
-
...preparsedExpression,
|
|
317
|
-
location: ast.sourceLocation(location),
|
|
318
|
-
};
|
|
319
|
-
return [ast.text(rawText, value, location)];
|
|
320
|
-
}
|
|
321
|
-
// Split the text node content arround expression and create node for each
|
|
322
|
-
const tokenizedContent = rawText.split(constants_2.EXPRESSION_RE);
|
|
323
|
-
for (const token of tokenizedContent) {
|
|
324
|
-
// Don't create nodes for emtpy strings
|
|
325
|
-
if (!token.length) {
|
|
326
|
-
continue;
|
|
327
|
-
}
|
|
328
|
-
let value;
|
|
329
|
-
if ((0, expression_1.isExpression)(token)) {
|
|
330
|
-
value = (0, expression_1.parseExpression)(ctx, token, ast.sourceLocation(location));
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
value = ast.literal((0, html_1.decodeTextContent)(token));
|
|
334
|
-
}
|
|
335
|
-
parsedTextNodes.push(ast.text(token, value, location));
|
|
336
|
-
}
|
|
337
|
-
return parsedTextNodes;
|
|
338
|
-
}
|
|
339
|
-
function parseComment(parse5Comment) {
|
|
340
|
-
const location = parse5Comment.sourceCodeLocation;
|
|
341
|
-
/* istanbul ignore if */
|
|
342
|
-
if (!location) {
|
|
343
|
-
// Parse5 will recover from invalid HTML. When this happens the node's sourceCodeLocation will be undefined.
|
|
344
|
-
// https://github.com/inikulin/parse5/blob/master/packages/parse5/docs/options/parser-options.md#sourcecodelocationinfo
|
|
345
|
-
// This is a defensive check as this should never happen for CommentNode.
|
|
346
|
-
throw new Error('An internal parsing error occurred during node creation; a comment node was found without a sourceCodeLocation.');
|
|
347
|
-
}
|
|
348
|
-
return ast.comment(parse5Comment.data, (0, html_1.decodeTextContent)(parse5Comment.data), location);
|
|
349
|
-
}
|
|
350
|
-
function getTemplateRoot(ctx, documentFragment) {
|
|
351
|
-
// Filter all the empty text nodes
|
|
352
|
-
const validRoots = documentFragment.childNodes.filter((child) => parse5Utils.isElementNode(child) ||
|
|
353
|
-
(parse5Utils.isTextNode(child) && child.value.trim().length));
|
|
354
|
-
if (validRoots.length > 1) {
|
|
355
|
-
const duplicateRoot = validRoots[1].sourceCodeLocation;
|
|
356
|
-
ctx.throw(errors_1.ParserDiagnostics.MULTIPLE_ROOTS_FOUND, [], duplicateRoot ? ast.sourceLocation(duplicateRoot) : duplicateRoot);
|
|
357
|
-
}
|
|
358
|
-
const [root] = validRoots;
|
|
359
|
-
if (!root || !parse5Utils.isElementNode(root)) {
|
|
360
|
-
ctx.throw(errors_1.ParserDiagnostics.MISSING_ROOT_TEMPLATE_TAG);
|
|
361
|
-
}
|
|
362
|
-
return root;
|
|
363
|
-
}
|
|
364
|
-
function applyHandlers(ctx, parsedAttr, element) {
|
|
365
|
-
let eventHandlerAttribute;
|
|
366
|
-
while ((eventHandlerAttribute = parsedAttr.pick(constants_2.EVENT_HANDLER_RE))) {
|
|
367
|
-
const { name } = eventHandlerAttribute;
|
|
368
|
-
if (!ast.isExpression(eventHandlerAttribute.value)) {
|
|
369
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.EVENT_HANDLER_SHOULD_BE_EXPRESSION, eventHandlerAttribute);
|
|
370
|
-
}
|
|
371
|
-
if (!name.match(constants_2.EVENT_HANDLER_NAME_RE)) {
|
|
372
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_EVENT_NAME, eventHandlerAttribute, [name]);
|
|
373
|
-
}
|
|
374
|
-
// Strip the `on` prefix from the event handler name
|
|
375
|
-
const eventName = name.slice(2);
|
|
376
|
-
const listener = ast.eventListener(eventName, eventHandlerAttribute.value, eventHandlerAttribute.location);
|
|
377
|
-
element.listeners.push(listener);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
function parseIf(ctx, parse5Elm, parse5ElmLocation, parent, parsedAttr) {
|
|
381
|
-
const ifAttributes = parsedAttr.pickAll(constants_2.IF_RE);
|
|
382
|
-
if (ifAttributes.length === 0) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
for (let i = 1; i < ifAttributes.length; i++) {
|
|
386
|
-
ctx.warnAtLocation(errors_1.ParserDiagnostics.SINGLE_IF_DIRECTIVE_ALLOWED, ast.sourceLocation(parse5ElmLocation), [parse5Elm.tagName]);
|
|
387
|
-
}
|
|
388
|
-
const ifAttribute = ifAttributes[0];
|
|
389
|
-
// if:true cannot be used with lwc:if, lwc:elseif, lwc:else
|
|
390
|
-
const incompatibleDirective = ctx.findInCurrentElementScope(ast.isConditionalBlock);
|
|
391
|
-
if (incompatibleDirective) {
|
|
392
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_IF_CANNOT_BE_USED_WITH_IF_DIRECTIVE, ast.sourceLocation(parse5ElmLocation), [ifAttribute.name]);
|
|
393
|
-
}
|
|
394
|
-
if (!ast.isExpression(ifAttribute.value)) {
|
|
395
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.IF_DIRECTIVE_SHOULD_BE_EXPRESSION, ifAttribute);
|
|
396
|
-
}
|
|
397
|
-
const [, modifier] = ifAttribute.name.split(':');
|
|
398
|
-
if (!constants_2.VALID_IF_MODIFIER.has(modifier)) {
|
|
399
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.UNEXPECTED_IF_MODIFIER, ifAttribute, [modifier]);
|
|
400
|
-
}
|
|
401
|
-
const node = ast.ifNode(modifier, ifAttribute.value, ast.sourceLocation(parse5ElmLocation), ifAttribute.location);
|
|
402
|
-
ctx.addNodeCurrentElementScope(node);
|
|
403
|
-
parent.children.push(node);
|
|
404
|
-
return node;
|
|
405
|
-
}
|
|
406
|
-
function parseIfBlock(ctx, _parse5Elm, parse5ElmLocation, parent, parsedAttr) {
|
|
407
|
-
const ifBlockAttribute = parsedAttr.pick('lwc:if');
|
|
408
|
-
if (!ifBlockAttribute) {
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
if (!ast.isExpression(ifBlockAttribute.value)) {
|
|
412
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.IF_BLOCK_DIRECTIVE_SHOULD_BE_EXPRESSION, ifBlockAttribute);
|
|
413
|
-
}
|
|
414
|
-
// An if block always starts a new chain.
|
|
415
|
-
if (ctx.isParsingSiblingIfBlock()) {
|
|
416
|
-
ctx.endIfChain();
|
|
417
|
-
}
|
|
418
|
-
const ifNode = ast.ifBlockNode(ifBlockAttribute.value, ast.sourceLocation(parse5ElmLocation), ifBlockAttribute.location);
|
|
419
|
-
ctx.addNodeCurrentElementScope(ifNode);
|
|
420
|
-
ctx.beginIfChain(ifNode);
|
|
421
|
-
parent.children.push(ifNode);
|
|
422
|
-
return ifNode;
|
|
423
|
-
}
|
|
424
|
-
function parseElseifBlock(ctx, _parse5Elm, parse5ElmLocation, _parent, parsedAttr) {
|
|
425
|
-
const elseifBlockAttribute = parsedAttr.pick('lwc:elseif');
|
|
426
|
-
if (!elseifBlockAttribute) {
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
const hasIfBlock = ctx.findInCurrentElementScope(ast.isIfBlock);
|
|
430
|
-
if (hasIfBlock) {
|
|
431
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.INVALID_IF_BLOCK_DIRECTIVE_WITH_CONDITIONAL, ast.sourceLocation(parse5ElmLocation), [elseifBlockAttribute.name]);
|
|
432
|
-
}
|
|
433
|
-
if (!ast.isExpression(elseifBlockAttribute.value)) {
|
|
434
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.ELSEIF_BLOCK_DIRECTIVE_SHOULD_BE_EXPRESSION, elseifBlockAttribute);
|
|
435
|
-
}
|
|
436
|
-
const conditionalParent = ctx.getSiblingIfNode();
|
|
437
|
-
if (!conditionalParent || !ast.isConditionalParentBlock(conditionalParent)) {
|
|
438
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_IF_SCOPE_NOT_FOUND, ast.sourceLocation(parse5ElmLocation), [elseifBlockAttribute.name]);
|
|
439
|
-
}
|
|
440
|
-
const elseifNode = ast.elseifBlockNode(elseifBlockAttribute.value, ast.sourceLocation(parse5ElmLocation), elseifBlockAttribute.location);
|
|
441
|
-
// Attach the node as a child of the preceding IfBlock
|
|
442
|
-
ctx.addNodeCurrentElementScope(elseifNode);
|
|
443
|
-
ctx.appendToIfChain(elseifNode);
|
|
444
|
-
conditionalParent.else = elseifNode;
|
|
445
|
-
return elseifNode;
|
|
446
|
-
}
|
|
447
|
-
function parseElseBlock(ctx, _parse5Elm, parse5ElmLocation, _parent, parsedAttr) {
|
|
448
|
-
const elseBlockAttribute = parsedAttr.pick('lwc:else');
|
|
449
|
-
if (!elseBlockAttribute) {
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
// Cannot be used with lwc:if on the same element
|
|
453
|
-
const hasIfBlock = ctx.findInCurrentElementScope(ast.isIfBlock);
|
|
454
|
-
if (hasIfBlock) {
|
|
455
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.INVALID_IF_BLOCK_DIRECTIVE_WITH_CONDITIONAL, ast.sourceLocation(parse5ElmLocation), [elseBlockAttribute.name]);
|
|
456
|
-
}
|
|
457
|
-
// Cannot be used with lwc:elseif on the same element
|
|
458
|
-
const hasElseifBlock = ctx.findInCurrentElementScope(ast.isElseifBlock);
|
|
459
|
-
if (hasElseifBlock) {
|
|
460
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.INVALID_ELSEIF_BLOCK_DIRECTIVE_WITH_CONDITIONAL, ast.sourceLocation(parse5ElmLocation), [elseBlockAttribute.name]);
|
|
461
|
-
}
|
|
462
|
-
// Must be used immediately after an lwc:if or lwc:elseif
|
|
463
|
-
const conditionalParent = ctx.getSiblingIfNode();
|
|
464
|
-
if (!conditionalParent || !ast.isConditionalParentBlock(conditionalParent)) {
|
|
465
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_IF_SCOPE_NOT_FOUND, ast.sourceLocation(parse5ElmLocation), [elseBlockAttribute.name]);
|
|
466
|
-
}
|
|
467
|
-
// Must not have a value
|
|
468
|
-
if (!ast.isBooleanLiteral(elseBlockAttribute.value)) {
|
|
469
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.ELSE_BLOCK_DIRECTIVE_CANNOT_HAVE_VALUE, ast.sourceLocation(parse5ElmLocation));
|
|
470
|
-
}
|
|
471
|
-
const elseNode = ast.elseBlockNode(ast.sourceLocation(parse5ElmLocation), elseBlockAttribute.location);
|
|
472
|
-
// Attach the node as a child of the preceding IfBlock
|
|
473
|
-
ctx.addNodeCurrentElementScope(elseNode);
|
|
474
|
-
// Avoid ending the if-chain until we finish parsing all children
|
|
475
|
-
ctx.appendToIfChain(elseNode);
|
|
476
|
-
conditionalParent.else = elseNode;
|
|
477
|
-
return elseNode;
|
|
478
|
-
}
|
|
479
|
-
function applyRootLwcDirectives(ctx, parsedAttr, root) {
|
|
480
|
-
const lwcAttribute = parsedAttr.get(constants_2.LWC_RE);
|
|
481
|
-
if (!lwcAttribute) {
|
|
482
|
-
return;
|
|
483
|
-
}
|
|
484
|
-
applyLwcRenderModeDirective(ctx, parsedAttr, root);
|
|
485
|
-
applyLwcPreserveCommentsDirective(ctx, parsedAttr, root);
|
|
486
|
-
}
|
|
487
|
-
function applyLwcRenderModeDirective(ctx, parsedAttr, root) {
|
|
488
|
-
const lwcRenderModeAttribute = parsedAttr.pick(types_1.RootDirectiveName.RenderMode);
|
|
489
|
-
if (!lwcRenderModeAttribute) {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
const { value: renderDomAttr } = lwcRenderModeAttribute;
|
|
493
|
-
if (!ast.isStringLiteral(renderDomAttr) ||
|
|
494
|
-
(renderDomAttr.value !== types_1.LWCDirectiveRenderMode.shadow &&
|
|
495
|
-
renderDomAttr.value !== types_1.LWCDirectiveRenderMode.light)) {
|
|
496
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_RENDER_MODE_INVALID_VALUE, root);
|
|
497
|
-
}
|
|
498
|
-
root.directives.push(ast.renderModeDirective(renderDomAttr.value, lwcRenderModeAttribute.location));
|
|
499
|
-
}
|
|
500
|
-
function applyLwcPreserveCommentsDirective(ctx, parsedAttr, root) {
|
|
501
|
-
const lwcPreserveCommentAttribute = parsedAttr.pick(types_1.RootDirectiveName.PreserveComments);
|
|
502
|
-
if (!lwcPreserveCommentAttribute) {
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
const { value: lwcPreserveCommentsAttr } = lwcPreserveCommentAttribute;
|
|
506
|
-
if (!ast.isBooleanLiteral(lwcPreserveCommentsAttr)) {
|
|
507
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.PRESERVE_COMMENTS_MUST_BE_BOOLEAN, root);
|
|
508
|
-
}
|
|
509
|
-
root.directives.push(ast.preserveCommentsDirective(lwcPreserveCommentsAttr.value, lwcPreserveCommentAttribute.location));
|
|
510
|
-
}
|
|
511
|
-
const LWC_DIRECTIVE_PROCESSORS = [
|
|
512
|
-
applyLwcExternalDirective,
|
|
513
|
-
applyLwcDynamicDirective,
|
|
514
|
-
applyLwcIsDirective,
|
|
515
|
-
applyLwcDomDirective,
|
|
516
|
-
applyLwcInnerHtmlDirective,
|
|
517
|
-
applyRefDirective,
|
|
518
|
-
applyLwcSpreadDirective,
|
|
519
|
-
applyLwcSlotBindDirective,
|
|
520
|
-
];
|
|
521
|
-
function applyLwcDirectives(ctx, parsedAttr, element) {
|
|
522
|
-
const lwcAttribute = parsedAttr.get(constants_2.LWC_RE);
|
|
523
|
-
if (!lwcAttribute) {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
if (!constants_2.LWC_DIRECTIVE_SET.has(lwcAttribute.name)) {
|
|
527
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.UNKNOWN_LWC_DIRECTIVE, element, [
|
|
528
|
-
lwcAttribute.name,
|
|
529
|
-
`<${element.name}>`,
|
|
530
|
-
]);
|
|
531
|
-
}
|
|
532
|
-
// Should not allow render mode or preserve comments on non root nodes
|
|
533
|
-
if (parsedAttr.get(types_1.RootDirectiveName.RenderMode)) {
|
|
534
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.UNKNOWN_LWC_DIRECTIVE, element, [
|
|
535
|
-
types_1.RootDirectiveName.RenderMode,
|
|
536
|
-
`<${element.name}>`,
|
|
537
|
-
]);
|
|
538
|
-
}
|
|
539
|
-
if (parsedAttr.get(types_1.RootDirectiveName.PreserveComments)) {
|
|
540
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.UNKNOWN_LWC_DIRECTIVE, element, [
|
|
541
|
-
types_1.RootDirectiveName.PreserveComments,
|
|
542
|
-
`<${element.name}>`,
|
|
543
|
-
]);
|
|
544
|
-
}
|
|
545
|
-
// Bind LWC directives to element
|
|
546
|
-
for (const matchAndApply of LWC_DIRECTIVE_PROCESSORS) {
|
|
547
|
-
matchAndApply(ctx, parsedAttr, element);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
function applyLwcSlotBindDirective(ctx, parsedAttr, element) {
|
|
551
|
-
const { name: tag } = element;
|
|
552
|
-
const slotBindAttribute = parsedAttr.pick(types_1.ElementDirectiveName.SlotBind);
|
|
553
|
-
if (!slotBindAttribute) {
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
if (!ast.isSlot(element)) {
|
|
557
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_LWC_SLOT_BIND_NON_SLOT_ELEMENT, element, [
|
|
558
|
-
`<${tag}>`,
|
|
559
|
-
]);
|
|
560
|
-
}
|
|
561
|
-
const { value: slotBindValue } = slotBindAttribute;
|
|
562
|
-
if (!ast.isExpression(slotBindValue)) {
|
|
563
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_LWC_SLOT_BIND_LITERAL_PROP, element, [
|
|
564
|
-
`<${tag}>`,
|
|
565
|
-
]);
|
|
566
|
-
}
|
|
567
|
-
element.directives.push(ast.slotBindDirective(slotBindValue, slotBindAttribute.location));
|
|
568
|
-
}
|
|
569
|
-
function applyLwcSpreadDirective(ctx, parsedAttr, element) {
|
|
570
|
-
const { name: tag } = element;
|
|
571
|
-
const lwcSpread = parsedAttr.pick(types_1.ElementDirectiveName.Spread);
|
|
572
|
-
if (!lwcSpread) {
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
if (!ctx.config.enableLwcSpread) {
|
|
576
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_OPTS_LWC_SPREAD, element);
|
|
577
|
-
}
|
|
578
|
-
const { value: lwcSpreadAttr } = lwcSpread;
|
|
579
|
-
if (!ast.isExpression(lwcSpreadAttr)) {
|
|
580
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_LWC_SPREAD_LITERAL_PROP, element, [`<${tag}>`]);
|
|
581
|
-
}
|
|
582
|
-
element.directives.push(ast.spreadDirective(lwcSpreadAttr, lwcSpread.location));
|
|
583
|
-
}
|
|
584
|
-
function applyLwcExternalDirective(ctx, parsedAttr, element) {
|
|
585
|
-
const lwcExternalAttribute = parsedAttr.pick(types_1.ElementDirectiveName.External);
|
|
586
|
-
if (!lwcExternalAttribute) {
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
if (!ast.isExternalComponent(element)) {
|
|
590
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_LWC_EXTERNAL_ON_NON_CUSTOM_ELEMENT, element, [
|
|
591
|
-
`<${element.name}>`,
|
|
592
|
-
]);
|
|
593
|
-
}
|
|
594
|
-
if (!ast.isBooleanLiteral(lwcExternalAttribute.value)) {
|
|
595
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_LWC_EXTERNAL_VALUE, element, [
|
|
596
|
-
`<${element.name}>`,
|
|
597
|
-
]);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
function applyLwcDynamicDirective(ctx, parsedAttr, element) {
|
|
601
|
-
const { name: tag } = element;
|
|
602
|
-
const lwcDynamicAttribute = parsedAttr.pick(types_1.ElementDirectiveName.Dynamic);
|
|
603
|
-
if (!lwcDynamicAttribute) {
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
if (!ctx.config.experimentalDynamicDirective) {
|
|
607
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_OPTS_LWC_DYNAMIC, element);
|
|
608
|
-
}
|
|
609
|
-
if (!ast.isComponent(element)) {
|
|
610
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_LWC_DYNAMIC_ON_NATIVE_ELEMENT, element, [
|
|
611
|
-
`<${tag}>`,
|
|
612
|
-
]);
|
|
613
|
-
}
|
|
614
|
-
const { value: lwcDynamicAttr, location } = lwcDynamicAttribute;
|
|
615
|
-
if (!ast.isExpression(lwcDynamicAttr)) {
|
|
616
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_LWC_DYNAMIC_LITERAL_PROP, element, [`<${tag}>`]);
|
|
617
|
-
}
|
|
618
|
-
// lwc:dynamic will be deprecated in 246, issue a warning when usage is detected.
|
|
619
|
-
ctx.warnOnNode(errors_1.ParserDiagnostics.DEPRECATED_LWC_DYNAMIC_ATTRIBUTE, element);
|
|
620
|
-
element.directives.push(ast.dynamicDirective(lwcDynamicAttr, location));
|
|
621
|
-
}
|
|
622
|
-
function applyLwcIsDirective(ctx, parsedAttr, element) {
|
|
623
|
-
const { name: tag } = element;
|
|
624
|
-
const lwcIsAttribute = parsedAttr.pick(types_1.ElementDirectiveName.Is);
|
|
625
|
-
if (!lwcIsAttribute) {
|
|
626
|
-
return;
|
|
627
|
-
}
|
|
628
|
-
if (!ast.isLwcComponent(element)) {
|
|
629
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_IS_INVALID_ELEMENT, element, [`<${tag}>`]);
|
|
630
|
-
}
|
|
631
|
-
const { value: lwcIsAttrValue, location } = lwcIsAttribute;
|
|
632
|
-
if (!ast.isExpression(lwcIsAttrValue)) {
|
|
633
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_LWC_IS_DIRECTIVE_VALUE, element, [
|
|
634
|
-
lwcIsAttrValue.value,
|
|
635
|
-
]);
|
|
636
|
-
}
|
|
637
|
-
element.directives.push(ast.lwcIsDirective(lwcIsAttrValue, location));
|
|
638
|
-
}
|
|
639
|
-
function applyLwcDomDirective(ctx, parsedAttr, element) {
|
|
640
|
-
const { name: tag } = element;
|
|
641
|
-
const lwcDomAttribute = parsedAttr.pick('lwc:dom');
|
|
642
|
-
if (!lwcDomAttribute) {
|
|
643
|
-
return;
|
|
644
|
-
}
|
|
645
|
-
if (ctx.renderMode === types_1.LWCDirectiveRenderMode.light) {
|
|
646
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_IN_LIGHT_DOM, element, [`<${tag}>`]);
|
|
647
|
-
}
|
|
648
|
-
if (ast.isComponent(element)) {
|
|
649
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_CUSTOM_ELEMENT, element, [`<${tag}>`]);
|
|
650
|
-
}
|
|
651
|
-
if (ast.isSlot(element)) {
|
|
652
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_SLOT_ELEMENT, element);
|
|
653
|
-
}
|
|
654
|
-
const { value: lwcDomAttr } = lwcDomAttribute;
|
|
655
|
-
if (!ast.isStringLiteral(lwcDomAttr) || lwcDomAttr.value !== types_1.LWCDirectiveDomMode.manual) {
|
|
656
|
-
const possibleValues = Object.keys(types_1.LWCDirectiveDomMode)
|
|
657
|
-
.map((value) => `"${value}"`)
|
|
658
|
-
.join(', or ');
|
|
659
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_VALUE, element, [possibleValues]);
|
|
660
|
-
}
|
|
661
|
-
element.directives.push(ast.domDirective(lwcDomAttr.value, lwcDomAttribute.location));
|
|
662
|
-
}
|
|
663
|
-
function applyLwcInnerHtmlDirective(ctx, parsedAttr, element) {
|
|
664
|
-
const lwcInnerHtmlDirective = parsedAttr.pick(types_1.ElementDirectiveName.InnerHTML);
|
|
665
|
-
if (!lwcInnerHtmlDirective) {
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
if (ast.isComponent(element) || ast.isLwcComponent(element)) {
|
|
669
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_CUSTOM_ELEMENT, element, [
|
|
670
|
-
`<${element.name}>`,
|
|
671
|
-
]);
|
|
672
|
-
}
|
|
673
|
-
if (ast.isSlot(element)) {
|
|
674
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_ELEMENT, element, [
|
|
675
|
-
`<${element.name}>`,
|
|
676
|
-
]);
|
|
677
|
-
}
|
|
678
|
-
const { value: innerHTMLVal } = lwcInnerHtmlDirective;
|
|
679
|
-
if (!ast.isStringLiteral(innerHTMLVal) && !ast.isExpression(innerHTMLVal)) {
|
|
680
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_VALUE, element, [
|
|
681
|
-
`<${element.name}>`,
|
|
682
|
-
]);
|
|
683
|
-
}
|
|
684
|
-
element.directives.push(ast.innerHTMLDirective(innerHTMLVal, lwcInnerHtmlDirective.location));
|
|
685
|
-
}
|
|
686
|
-
function applyRefDirective(ctx, parsedAttr, element) {
|
|
687
|
-
const lwcRefDirective = parsedAttr.pick(types_1.ElementDirectiveName.Ref);
|
|
688
|
-
if (!lwcRefDirective) {
|
|
689
|
-
return;
|
|
690
|
-
}
|
|
691
|
-
if (ast.isSlot(element)) {
|
|
692
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_REF_INVALID_ELEMENT, element, [`<${element.name}>`]);
|
|
693
|
-
}
|
|
694
|
-
if (isInIteration(ctx)) {
|
|
695
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_REF_INVALID_LOCATION_INSIDE_ITERATION, element, [
|
|
696
|
-
`<${element.name}>`,
|
|
697
|
-
]);
|
|
698
|
-
}
|
|
699
|
-
const { value: refName } = lwcRefDirective;
|
|
700
|
-
if (!ast.isStringLiteral(refName) || refName.value.length === 0) {
|
|
701
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_REF_INVALID_VALUE, element, [`<${element.name}>`]);
|
|
702
|
-
}
|
|
703
|
-
element.directives.push(ast.refDirective(refName, lwcRefDirective.location));
|
|
704
|
-
}
|
|
705
|
-
function parseForEach(ctx, _parse5Elm, parse5ElmLocation, parent, parsedAttr) {
|
|
706
|
-
const forEachAttribute = parsedAttr.pick('for:each');
|
|
707
|
-
const forItemAttribute = parsedAttr.pick('for:item');
|
|
708
|
-
const forIndex = parsedAttr.pick('for:index');
|
|
709
|
-
if (forEachAttribute && forItemAttribute) {
|
|
710
|
-
if (!ast.isExpression(forEachAttribute.value)) {
|
|
711
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.FOR_EACH_DIRECTIVE_SHOULD_BE_EXPRESSION, forEachAttribute);
|
|
712
|
-
}
|
|
713
|
-
const forItemValue = forItemAttribute.value;
|
|
714
|
-
if (!ast.isStringLiteral(forItemValue)) {
|
|
715
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.FOR_ITEM_DIRECTIVE_SHOULD_BE_STRING, forItemAttribute);
|
|
716
|
-
}
|
|
717
|
-
const item = (0, expression_1.parseIdentifier)(ctx, forItemValue.value, forItemAttribute.location);
|
|
718
|
-
let index;
|
|
719
|
-
if (forIndex) {
|
|
720
|
-
const forIndexValue = forIndex.value;
|
|
721
|
-
if (!ast.isStringLiteral(forIndexValue)) {
|
|
722
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.FOR_INDEX_DIRECTIVE_SHOULD_BE_STRING, forIndex);
|
|
723
|
-
}
|
|
724
|
-
index = (0, expression_1.parseIdentifier)(ctx, forIndexValue.value, forIndex.location);
|
|
725
|
-
}
|
|
726
|
-
const node = ast.forEach(forEachAttribute.value, ast.sourceLocation(parse5ElmLocation), forEachAttribute.location, item, index);
|
|
727
|
-
ctx.addNodeCurrentElementScope(node);
|
|
728
|
-
parent.children.push(node);
|
|
729
|
-
return node;
|
|
730
|
-
}
|
|
731
|
-
else if (forEachAttribute || forItemAttribute) {
|
|
732
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.FOR_EACH_AND_FOR_ITEM_DIRECTIVES_SHOULD_BE_TOGETHER, ast.sourceLocation(parse5ElmLocation));
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
function parseForOf(ctx, _parse5Elm, parse5ElmLocation, parent, parsedAttr) {
|
|
736
|
-
const iteratorExpression = parsedAttr.pick(constants_2.ITERATOR_RE);
|
|
737
|
-
if (!iteratorExpression) {
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
const hasForEach = ctx.findInCurrentElementScope(ast.isForEach);
|
|
741
|
-
if (hasForEach) {
|
|
742
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.INVALID_FOR_EACH_WITH_ITERATOR, ast.sourceLocation(parse5ElmLocation), [iteratorExpression.name]);
|
|
743
|
-
}
|
|
744
|
-
const iteratorAttributeName = iteratorExpression.name;
|
|
745
|
-
const [, iteratorName] = iteratorAttributeName.split(':');
|
|
746
|
-
if (!ast.isExpression(iteratorExpression.value)) {
|
|
747
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.DIRECTIVE_SHOULD_BE_EXPRESSION, iteratorExpression, [
|
|
748
|
-
iteratorExpression.name,
|
|
749
|
-
]);
|
|
750
|
-
}
|
|
751
|
-
const iterator = (0, expression_1.parseIdentifier)(ctx, iteratorName, iteratorExpression.location);
|
|
752
|
-
const node = ast.forOf(iteratorExpression.value, iterator, ast.sourceLocation(parse5ElmLocation), iteratorExpression.location);
|
|
753
|
-
ctx.addNodeCurrentElementScope(node);
|
|
754
|
-
parent.children.push(node);
|
|
755
|
-
return node;
|
|
756
|
-
}
|
|
757
|
-
function parseScopedSlotFragment(ctx, parse5Elm, parse5ElmLocation, parent, parsedAttr) {
|
|
758
|
-
const slotDataAttr = parsedAttr.pick(types_1.ElementDirectiveName.SlotData);
|
|
759
|
-
if (!slotDataAttr) {
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
if (parse5Elm.tagName !== 'template') {
|
|
763
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.SCOPED_SLOT_DATA_ON_TEMPLATE_ONLY, slotDataAttr);
|
|
764
|
-
}
|
|
765
|
-
// 'lwc:slot-data' cannot be combined with other directives on the same <template> tag
|
|
766
|
-
if (ctx.findInCurrentElementScope(ast.isElementDirective)) {
|
|
767
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.SCOPED_SLOTDATA_CANNOT_BE_COMBINED_WITH_OTHER_DIRECTIVE, ast.sourceLocation(parse5ElmLocation));
|
|
768
|
-
}
|
|
769
|
-
// <template lwc:slot-data> element should always be the direct child of a custom element
|
|
770
|
-
// The only exception is, a conditional block as parent
|
|
771
|
-
const parentCmp = ctx.findAncestor(ast.isComponent, ({ current }) => current && ast.isConditionalBlock(current));
|
|
772
|
-
if (!parentCmp) {
|
|
773
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.INVALID_PARENT_OF_LWC_SLOT_DATA, ast.sourceLocation(parse5ElmLocation));
|
|
774
|
-
}
|
|
775
|
-
const slotDataAttrValue = slotDataAttr.value;
|
|
776
|
-
if (!ast.isStringLiteral(slotDataAttrValue)) {
|
|
777
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.SLOT_DATA_VALUE_SHOULD_BE_STRING, slotDataAttr);
|
|
778
|
-
}
|
|
779
|
-
// Extract name (literal or bound) of slot if in case it's a named slot
|
|
780
|
-
const slotAttr = parsedAttr.pick('slot');
|
|
781
|
-
let slotName;
|
|
782
|
-
if (slotAttr) {
|
|
783
|
-
slotName = slotAttr.value;
|
|
784
|
-
}
|
|
785
|
-
const identifier = (0, expression_1.parseIdentifier)(ctx, slotDataAttrValue.value, slotDataAttr.location);
|
|
786
|
-
const node = ast.scopedSlotFragment(identifier, ast.sourceLocation(parse5ElmLocation), slotDataAttr.location, slotName !== null && slotName !== void 0 ? slotName : ast.literal(''));
|
|
787
|
-
ctx.addNodeCurrentElementScope(node);
|
|
788
|
-
parent.children.push(node);
|
|
789
|
-
return node;
|
|
790
|
-
}
|
|
791
|
-
function applyKey(ctx, parsedAttr, element) {
|
|
792
|
-
const { name: tag } = element;
|
|
793
|
-
const keyAttribute = parsedAttr.pick(types_1.ElementDirectiveName.Key);
|
|
794
|
-
if (keyAttribute) {
|
|
795
|
-
if (!ast.isExpression(keyAttribute.value)) {
|
|
796
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.KEY_ATTRIBUTE_SHOULD_BE_EXPRESSION, keyAttribute);
|
|
797
|
-
}
|
|
798
|
-
const forOfParent = getForOfParent(ctx);
|
|
799
|
-
const forEachParent = getForEachParent(ctx);
|
|
800
|
-
if (forOfParent) {
|
|
801
|
-
if (attributeExpressionReferencesForOfIndex(keyAttribute, forOfParent)) {
|
|
802
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.KEY_SHOULDNT_REFERENCE_ITERATOR_INDEX, keyAttribute, [tag]);
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
else if (forEachParent) {
|
|
806
|
-
if (attributeExpressionReferencesForEachIndex(keyAttribute, forEachParent)) {
|
|
807
|
-
const name = 'name' in keyAttribute.value && keyAttribute.value.name;
|
|
808
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.KEY_SHOULDNT_REFERENCE_FOR_EACH_INDEX, keyAttribute, [tag, name]);
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
if (forOfParent || forEachParent) {
|
|
812
|
-
element.directives.push(ast.keyDirective(keyAttribute.value, keyAttribute.location));
|
|
813
|
-
}
|
|
814
|
-
else {
|
|
815
|
-
ctx.warnOnNode(errors_1.ParserDiagnostics.KEY_SHOULD_BE_IN_ITERATION, keyAttribute, [tag]);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
else if (isInIteratorElement(ctx)) {
|
|
819
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.MISSING_KEY_IN_ITERATOR, element, [tag]);
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
const RESTRICTED_DIRECTIVES_ON_SLOT = Object.values(types_1.TemplateDirectiveName).join(', ');
|
|
823
|
-
const ALLOWED_SLOT_ATTRIBUTES = [
|
|
824
|
-
types_1.ElementDirectiveName.Key,
|
|
825
|
-
types_1.ElementDirectiveName.SlotBind,
|
|
826
|
-
'name',
|
|
827
|
-
'slot',
|
|
828
|
-
];
|
|
829
|
-
const ALLOWED_SLOT_ATTRIBUTES_SET = new Set(ALLOWED_SLOT_ATTRIBUTES);
|
|
830
|
-
function parseSlot(ctx, parsedAttr, parse5ElmLocation) {
|
|
831
|
-
const location = ast.sourceLocation(parse5ElmLocation);
|
|
832
|
-
const isScopedSlot = !(0, shared_1.isUndefined)(parsedAttr.get(types_1.ElementDirectiveName.SlotBind));
|
|
833
|
-
if (isScopedSlot && ctx.renderMode !== types_1.LWCDirectiveRenderMode.light) {
|
|
834
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.SCOPED_SLOT_BIND_IN_LIGHT_DOM_ONLY, location);
|
|
835
|
-
}
|
|
836
|
-
// Restrict specific template directives on <slot> element
|
|
837
|
-
const hasDirectives = ctx.findInCurrentElementScope(ast.isElementDirective);
|
|
838
|
-
if (hasDirectives) {
|
|
839
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.SLOT_TAG_CANNOT_HAVE_DIRECTIVES, location, [
|
|
840
|
-
RESTRICTED_DIRECTIVES_ON_SLOT,
|
|
841
|
-
]);
|
|
842
|
-
}
|
|
843
|
-
// Can't handle slots in applySlot because it would be too late for class and style attrs
|
|
844
|
-
if (ctx.renderMode === types_1.LWCDirectiveRenderMode.light) {
|
|
845
|
-
const invalidAttrs = parsedAttr
|
|
846
|
-
.getAttributes()
|
|
847
|
-
.filter(({ name }) => !ALLOWED_SLOT_ATTRIBUTES_SET.has(name))
|
|
848
|
-
.map(({ name }) => name);
|
|
849
|
-
if (invalidAttrs.length) {
|
|
850
|
-
// Light DOM slots cannot have events because there's no actual `<slot>` element
|
|
851
|
-
const eventHandler = invalidAttrs.find((name) => name.match(constants_2.EVENT_HANDLER_NAME_RE));
|
|
852
|
-
if (eventHandler) {
|
|
853
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_LIGHT_SLOT_INVALID_EVENT_LISTENER, location, [eventHandler]);
|
|
854
|
-
}
|
|
855
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_LIGHT_SLOT_INVALID_ATTRIBUTES, location, [
|
|
856
|
-
invalidAttrs.join(','),
|
|
857
|
-
ALLOWED_SLOT_ATTRIBUTES.join(', '),
|
|
858
|
-
]);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
// Default slot have empty string name
|
|
862
|
-
let name = '';
|
|
863
|
-
const nameAttribute = parsedAttr.get('name');
|
|
864
|
-
if (nameAttribute) {
|
|
865
|
-
if (ast.isExpression(nameAttribute.value)) {
|
|
866
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.NAME_ON_SLOT_CANNOT_BE_EXPRESSION, nameAttribute);
|
|
867
|
-
}
|
|
868
|
-
else if (ast.isStringLiteral(nameAttribute.value)) {
|
|
869
|
-
name = nameAttribute.value.value;
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
const seenInContext = ctx.hasSeenSlot(name);
|
|
873
|
-
ctx.addSeenSlot(name);
|
|
874
|
-
if (seenInContext) {
|
|
875
|
-
// Scoped slots do not allow duplicate or mixed slots
|
|
876
|
-
// https://rfcs.lwc.dev/rfcs/lwc/0118-scoped-slots-light-dom#restricting-ambigious-bindings
|
|
877
|
-
// https://rfcs.lwc.dev/rfcs/lwc/0118-scoped-slots-light-dom#invalid-usages
|
|
878
|
-
// Note: ctx.seenScopedSlots is not "if" context aware and it does not need to be.
|
|
879
|
-
// It is only responsible to determine if a scoped slot with the same name has been seen prior.
|
|
880
|
-
if (ctx.seenScopedSlots.has(name)) {
|
|
881
|
-
// Differentiate between mixed type or duplicate scoped slot
|
|
882
|
-
const errorInfo = isScopedSlot
|
|
883
|
-
? errors_1.ParserDiagnostics.NO_DUPLICATE_SCOPED_SLOT // error
|
|
884
|
-
: errors_1.ParserDiagnostics.NO_MIXED_SLOT_TYPES; // error
|
|
885
|
-
ctx.throwAtLocation(errorInfo, location, [name === '' ? 'default' : `name="${name}"`]);
|
|
886
|
-
}
|
|
887
|
-
else {
|
|
888
|
-
// Differentiate between mixed type or duplicate standard slot
|
|
889
|
-
const errorInfo = isScopedSlot
|
|
890
|
-
? errors_1.ParserDiagnostics.NO_MIXED_SLOT_TYPES // error
|
|
891
|
-
: errors_1.ParserDiagnostics.NO_DUPLICATE_SLOTS; // warning
|
|
892
|
-
// for standard slots, preserve old behavior of warnings
|
|
893
|
-
ctx.warnAtLocation(errorInfo, location, [name === '' ? 'default' : `name="${name}"`]);
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
else if (!isScopedSlot && isInIteration(ctx)) {
|
|
897
|
-
// Scoped slots are allowed to be placed in iteration blocks
|
|
898
|
-
ctx.warnAtLocation(errors_1.ParserDiagnostics.NO_SLOTS_IN_ITERATOR, location, [
|
|
899
|
-
name === '' ? 'default' : `name="${name}"`,
|
|
900
|
-
]);
|
|
901
|
-
}
|
|
902
|
-
if (isScopedSlot) {
|
|
903
|
-
ctx.seenScopedSlots.add(name);
|
|
904
|
-
}
|
|
905
|
-
return ast.slot(name, parse5ElmLocation);
|
|
906
|
-
}
|
|
907
|
-
function applyAttributes(ctx, parsedAttr, element) {
|
|
908
|
-
const { name: tag } = element;
|
|
909
|
-
const attributes = parsedAttr.getAttributes();
|
|
910
|
-
const properties = new Map();
|
|
911
|
-
for (const attr of attributes) {
|
|
912
|
-
const { name } = attr;
|
|
913
|
-
if (!(0, attribute_1.isValidHTMLAttribute)(tag, name)) {
|
|
914
|
-
ctx.warnOnNode(errors_1.ParserDiagnostics.INVALID_HTML_ATTRIBUTE, attr, [name, tag]);
|
|
915
|
-
}
|
|
916
|
-
if (name.match(/[^a-z0-9]$/)) {
|
|
917
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.ATTRIBUTE_NAME_MUST_END_WITH_ALPHA_NUMERIC_CHARACTER, attr, [name, tag]);
|
|
918
|
-
}
|
|
919
|
-
// The leading '-' is necessary to preserve attribute to property reflection as the '-' is a signal
|
|
920
|
-
// to the compiler to convert the first character following it to an uppercase.
|
|
921
|
-
// This is needed for property names with an @api annotation because they can begin with an upper case character.
|
|
922
|
-
if (!/^-*[a-z]|^[_$]/.test(name)) {
|
|
923
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.ATTRIBUTE_NAME_STARTS_WITH_INVALID_CHARACTER, attr, [
|
|
924
|
-
name,
|
|
925
|
-
tag,
|
|
926
|
-
]);
|
|
927
|
-
}
|
|
928
|
-
if (ast.isStringLiteral(attr.value)) {
|
|
929
|
-
if (name === 'id') {
|
|
930
|
-
const { value } = attr.value;
|
|
931
|
-
if (/\s+/.test(value)) {
|
|
932
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_ID_ATTRIBUTE, attr, [value]);
|
|
933
|
-
}
|
|
934
|
-
if (isInIteration(ctx)) {
|
|
935
|
-
ctx.warnOnNode(errors_1.ParserDiagnostics.INVALID_STATIC_ID_IN_ITERATION, attr);
|
|
936
|
-
}
|
|
937
|
-
if (ctx.seenIds.has(value)) {
|
|
938
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.DUPLICATE_ID_FOUND, attr, [value]);
|
|
939
|
-
}
|
|
940
|
-
else {
|
|
941
|
-
ctx.seenIds.add(value);
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
// the if branch handles
|
|
946
|
-
// 1. All attributes for standard elements except 1 case are handled as attributes
|
|
947
|
-
// 2. For custom elements, only key, slot and data are handled as attributes, rest as properties
|
|
948
|
-
if ((0, attribute_1.isAttribute)(element, name)) {
|
|
949
|
-
element.attributes.push(attr);
|
|
950
|
-
}
|
|
951
|
-
else {
|
|
952
|
-
const propName = (0, attribute_1.attributeToPropertyName)(name);
|
|
953
|
-
const existingProp = properties.get(propName);
|
|
954
|
-
if (existingProp) {
|
|
955
|
-
ctx.warnOnNode(errors_1.ParserDiagnostics.DUPLICATE_ATTR_PROP_TRANSFORM, attr, [
|
|
956
|
-
existingProp.attributeName,
|
|
957
|
-
name,
|
|
958
|
-
propName,
|
|
959
|
-
]);
|
|
960
|
-
}
|
|
961
|
-
properties.set(propName, ast.property(propName, name, attr.value, attr.location));
|
|
962
|
-
parsedAttr.pick(name);
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
element.properties.push(...properties.values());
|
|
966
|
-
}
|
|
967
|
-
function validateRoot(ctx, parsedAttr, root) {
|
|
968
|
-
const rootAttrs = parsedAttr.getAttributes();
|
|
969
|
-
if (rootAttrs.length) {
|
|
970
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.ROOT_TEMPLATE_HAS_UNKNOWN_ATTRIBUTES, root, [
|
|
971
|
-
rootAttrs.map(({ name }) => name).join(','),
|
|
972
|
-
]);
|
|
973
|
-
}
|
|
974
|
-
if (!root.location.endTag) {
|
|
975
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.NO_MATCHING_CLOSING_TAGS, root, ['template']);
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
function validateElement(ctx, element, parse5Elm) {
|
|
979
|
-
const { tagName: tag, namespaceURI: namespace } = parse5Elm;
|
|
980
|
-
// Check if a non-void element has a matching closing tag.
|
|
981
|
-
//
|
|
982
|
-
// Note: Parse5 currently fails to collect end tag location for element with a tag name
|
|
983
|
-
// containing an upper case character (inikulin/parse5#352).
|
|
984
|
-
const hasClosingTag = Boolean(element.location.endTag);
|
|
985
|
-
if (!(0, shared_1.isVoidElement)(tag, namespace) &&
|
|
986
|
-
!hasClosingTag &&
|
|
987
|
-
tag === tag.toLocaleLowerCase() &&
|
|
988
|
-
namespace === shared_1.HTML_NAMESPACE) {
|
|
989
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.NO_MATCHING_CLOSING_TAGS, element, [tag]);
|
|
990
|
-
}
|
|
991
|
-
if (tag === 'style' && namespace === shared_1.HTML_NAMESPACE) {
|
|
992
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.STYLE_TAG_NOT_ALLOWED_IN_TEMPLATE, element);
|
|
993
|
-
}
|
|
994
|
-
else {
|
|
995
|
-
const isNotAllowedHtmlTag = constants_2.DISALLOWED_HTML_TAGS.has(tag);
|
|
996
|
-
if (namespace === shared_1.HTML_NAMESPACE && isNotAllowedHtmlTag) {
|
|
997
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.FORBIDDEN_TAG_ON_TEMPLATE, element, [tag]);
|
|
998
|
-
}
|
|
999
|
-
const isNotAllowedSvgTag = !constants_2.SUPPORTED_SVG_TAGS.has(tag);
|
|
1000
|
-
if (namespace === shared_1.SVG_NAMESPACE && isNotAllowedSvgTag) {
|
|
1001
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.FORBIDDEN_SVG_NAMESPACE_IN_TEMPLATE, element, [tag]);
|
|
1002
|
-
}
|
|
1003
|
-
const isNotAllowedMathMlTag = constants_2.DISALLOWED_MATHML_TAGS.has(tag);
|
|
1004
|
-
if (namespace === shared_1.MATHML_NAMESPACE && isNotAllowedMathMlTag) {
|
|
1005
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.FORBIDDEN_MATHML_NAMESPACE_IN_TEMPLATE, element, [
|
|
1006
|
-
tag,
|
|
1007
|
-
]);
|
|
1008
|
-
}
|
|
1009
|
-
const isKnownTag = ast.isComponent(element) ||
|
|
1010
|
-
ast.isExternalComponent(element) ||
|
|
1011
|
-
ast.isBaseLwcElement(element) ||
|
|
1012
|
-
constants_2.KNOWN_HTML_AND_SVG_ELEMENTS.has(tag) ||
|
|
1013
|
-
constants_2.SUPPORTED_SVG_TAGS.has(tag) ||
|
|
1014
|
-
constants_1.DASHED_TAGNAME_ELEMENT_SET.has(tag);
|
|
1015
|
-
if (!isKnownTag) {
|
|
1016
|
-
ctx.warnOnNode(errors_1.ParserDiagnostics.UNKNOWN_HTML_TAG_IN_TEMPLATE, element, [tag]);
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
function validateTemplate(ctx, parsedAttr, template, parse5ElmLocation) {
|
|
1021
|
-
const location = ast.sourceLocation(parse5ElmLocation);
|
|
1022
|
-
// Empty templates not allowed outside of root
|
|
1023
|
-
if (!template.attrs.length) {
|
|
1024
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.NO_DIRECTIVE_FOUND_ON_TEMPLATE, location);
|
|
1025
|
-
}
|
|
1026
|
-
if (parsedAttr.get(types_1.ElementDirectiveName.External)) {
|
|
1027
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.INVALID_LWC_EXTERNAL_ON_NON_CUSTOM_ELEMENT, location, ['<template>']);
|
|
1028
|
-
}
|
|
1029
|
-
if (parsedAttr.get(types_1.ElementDirectiveName.InnerHTML)) {
|
|
1030
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_ELEMENT, location, [
|
|
1031
|
-
'<template>',
|
|
1032
|
-
]);
|
|
1033
|
-
}
|
|
1034
|
-
if (parsedAttr.get(types_1.ElementDirectiveName.Ref)) {
|
|
1035
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_REF_INVALID_ELEMENT, location, ['<template>']);
|
|
1036
|
-
}
|
|
1037
|
-
if (parsedAttr.get(types_1.ElementDirectiveName.Is)) {
|
|
1038
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_IS_INVALID_ELEMENT, location, ['<template>']);
|
|
1039
|
-
}
|
|
1040
|
-
// At this point in the parsing all supported attributes from a non root template element
|
|
1041
|
-
// should have been removed from ParsedAttribute and all other attributes will be ignored.
|
|
1042
|
-
const invalidTemplateAttributes = parsedAttr.getAttributes();
|
|
1043
|
-
if (invalidTemplateAttributes.length) {
|
|
1044
|
-
ctx.warnAtLocation(errors_1.ParserDiagnostics.INVALID_TEMPLATE_ATTRIBUTE, location, [
|
|
1045
|
-
invalidTemplateAttributes.map((attr) => attr.name).join(', '),
|
|
1046
|
-
]);
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
function validateChildren(ctx, element, directive) {
|
|
1050
|
-
if (directive) {
|
|
1051
|
-
// Find a scoped slot fragment node if it exists
|
|
1052
|
-
const slotFragment = ctx.findAncestor(ast.isScopedSlotFragment, ({ current }) => current && ast.isComponent, directive);
|
|
1053
|
-
// If the current directive is a slotFragment or the descendent of a slotFragment, additional
|
|
1054
|
-
// validations are required
|
|
1055
|
-
if (!(0, shared_1.isNull)(slotFragment)) {
|
|
1056
|
-
/*
|
|
1057
|
-
* A slot fragment cannot contain comment or text node as children.
|
|
1058
|
-
* Comment and Text nodes are always slotted to the default slot, in other words these
|
|
1059
|
-
* nodes cannot be assigned to a named slot. This restriction is in place to ensure that
|
|
1060
|
-
* in the future if slotting is done via slot assignment API, we won't have named scoped
|
|
1061
|
-
* slot usecase that cannot be supported.
|
|
1062
|
-
*/
|
|
1063
|
-
directive.children.forEach((child) => {
|
|
1064
|
-
if ((ctx.preserveComments && ast.isComment(child)) || ast.isText(child)) {
|
|
1065
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.NON_ELEMENT_SCOPED_SLOT_CONTENT, child);
|
|
1066
|
-
}
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
if (!element) {
|
|
1071
|
-
return;
|
|
1072
|
-
}
|
|
1073
|
-
const effectiveChildren = ctx.preserveComments
|
|
1074
|
-
? element.children
|
|
1075
|
-
: element.children.filter((child) => !ast.isComment(child));
|
|
1076
|
-
const hasDomDirective = element.directives.find(ast.isDomDirective);
|
|
1077
|
-
if (hasDomDirective && effectiveChildren.length) {
|
|
1078
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_CONTENTS, element);
|
|
1079
|
-
}
|
|
1080
|
-
// prevents lwc:inner-html to be used in an element with content
|
|
1081
|
-
if (element.directives.find(ast.isInnerHTMLDirective) && effectiveChildren.length) {
|
|
1082
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_CONTENTS, element, [
|
|
1083
|
-
`<${element.name}>`,
|
|
1084
|
-
]);
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
function validateAttributes(ctx, parsedAttr, element) {
|
|
1088
|
-
const { name: tag } = element;
|
|
1089
|
-
const attributes = parsedAttr.getAttributes();
|
|
1090
|
-
for (const attr of attributes) {
|
|
1091
|
-
const { name: attrName, value: attrVal } = attr;
|
|
1092
|
-
if ((0, attribute_1.isProhibitedIsAttribute)(attrName)) {
|
|
1093
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.IS_ATTRIBUTE_NOT_SUPPORTED, element);
|
|
1094
|
-
}
|
|
1095
|
-
if ((0, attribute_1.isTabIndexAttribute)(attrName)) {
|
|
1096
|
-
if (!ast.isExpression(attrVal) && !(0, attribute_1.isValidTabIndexAttributeValue)(attrVal.value)) {
|
|
1097
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_TABINDEX_ATTRIBUTE, element);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
// TODO [#1136]: once the template compiler emits the element namespace information to the engine we should
|
|
1101
|
-
// restrict the validation of the "srcdoc" attribute on the "iframe" element only if this element is
|
|
1102
|
-
// part of the HTML namespace.
|
|
1103
|
-
if (tag === 'iframe' && attrName === 'srcdoc') {
|
|
1104
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.FORBIDDEN_IFRAME_SRCDOC_ATTRIBUTE, element);
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
function validateProperties(ctx, element) {
|
|
1109
|
-
for (const prop of element.properties) {
|
|
1110
|
-
const { attributeName: attrName, value } = prop;
|
|
1111
|
-
if ((0, attribute_1.isProhibitedIsAttribute)(attrName)) {
|
|
1112
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.IS_ATTRIBUTE_NOT_SUPPORTED, element);
|
|
1113
|
-
}
|
|
1114
|
-
if (
|
|
1115
|
-
// tabindex is transformed to tabIndex for properties
|
|
1116
|
-
(0, attribute_1.isTabIndexAttribute)(attrName) &&
|
|
1117
|
-
!ast.isExpression(value) &&
|
|
1118
|
-
!(0, attribute_1.isValidTabIndexAttributeValue)(value.value)) {
|
|
1119
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.INVALID_TABINDEX_ATTRIBUTE, element);
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
function parseAttributes(ctx, parse5Elm, parse5ElmLocation) {
|
|
1124
|
-
const parsedAttrs = new attribute_1.ParsedAttribute();
|
|
1125
|
-
const { attrs: attributes, tagName } = parse5Elm;
|
|
1126
|
-
const { attrs: attrLocations } = parse5ElmLocation;
|
|
1127
|
-
for (const attr of attributes) {
|
|
1128
|
-
const attrLocation = attrLocations === null || attrLocations === void 0 ? void 0 : attrLocations[(0, attribute_1.attributeName)(attr).toLowerCase()];
|
|
1129
|
-
/* istanbul ignore if */
|
|
1130
|
-
if (!attrLocation) {
|
|
1131
|
-
throw new Error('An internal parsing error occurred while parsing attributes; attributes were found without a location.');
|
|
1132
|
-
}
|
|
1133
|
-
parsedAttrs.append(getTemplateAttribute(ctx, tagName, attr, attrLocation));
|
|
1134
|
-
}
|
|
1135
|
-
return parsedAttrs;
|
|
1136
|
-
}
|
|
1137
|
-
function getTemplateAttribute(ctx, tag, attribute, attributeLocation) {
|
|
1138
|
-
// Convert attribute name to lowercase because the location map keys follow the algorithm defined in the spec
|
|
1139
|
-
// https://wicg.github.io/controls-list/html-output/multipage/syntax.html#attribute-name-state
|
|
1140
|
-
const rawAttribute = ctx.getSource(attributeLocation.startOffset, attributeLocation.endOffset);
|
|
1141
|
-
const location = ast.sourceLocation(attributeLocation);
|
|
1142
|
-
// parse5 automatically converts the casing from camel case to all lowercase. If the attribute name
|
|
1143
|
-
// is not the same before and after the parsing, then the attribute name contains capital letters
|
|
1144
|
-
const attrName = (0, attribute_1.attributeName)(attribute);
|
|
1145
|
-
if (!rawAttribute.startsWith(attrName)) {
|
|
1146
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.INVALID_ATTRIBUTE_CASE, location, [
|
|
1147
|
-
rawAttribute,
|
|
1148
|
-
tag,
|
|
1149
|
-
]);
|
|
1150
|
-
}
|
|
1151
|
-
const isBooleanAttribute = !rawAttribute.includes('=');
|
|
1152
|
-
const { value, escapedExpression } = (0, attribute_1.normalizeAttributeValue)(ctx, rawAttribute, tag, attribute, location);
|
|
1153
|
-
let attrValue;
|
|
1154
|
-
// TODO [#3370]: If complex template expressions are adopted, `preparsedJsExpressions`
|
|
1155
|
-
// should be checked. However, to avoid significant complications in the internal types,
|
|
1156
|
-
// arising from supporting both implementations simultaneously, we will re-parse the
|
|
1157
|
-
// expression here when `ctx.config.experimentalComplexExpressions` is true.
|
|
1158
|
-
if ((0, expression_1.isExpression)(value) && !escapedExpression) {
|
|
1159
|
-
attrValue = (0, expression_1.parseExpression)(ctx, value, location);
|
|
1160
|
-
}
|
|
1161
|
-
else if (isBooleanAttribute) {
|
|
1162
|
-
attrValue = ast.literal(true);
|
|
1163
|
-
}
|
|
1164
|
-
else {
|
|
1165
|
-
attrValue = ast.literal(value);
|
|
1166
|
-
}
|
|
1167
|
-
return ast.attribute(attrName, attrValue, location);
|
|
1168
|
-
}
|
|
1169
|
-
function isInIteration(ctx) {
|
|
1170
|
-
return !!ctx.findAncestor(ast.isForBlock);
|
|
1171
|
-
}
|
|
1172
|
-
function getForOfParent(ctx) {
|
|
1173
|
-
return ctx.findAncestor(ast.isForOf, ({ parent }) => parent && !ast.isBaseElement(parent));
|
|
1174
|
-
}
|
|
1175
|
-
function getForEachParent(ctx) {
|
|
1176
|
-
return ctx.findAncestor(ast.isForEach, ({ parent }) => parent && !ast.isBaseElement(parent));
|
|
1177
|
-
}
|
|
1178
|
-
function isInIteratorElement(ctx) {
|
|
1179
|
-
return !!(getForOfParent(ctx) || getForEachParent(ctx));
|
|
1180
|
-
}
|
|
1181
|
-
//# sourceMappingURL=index.js.map
|