@lwc/template-compiler 2.7.1 → 2.7.2
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/dist/commonjs/codegen/codegen.js +3 -73
- package/dist/commonjs/codegen/codegen.js.map +1 -1
- package/dist/commonjs/codegen/helpers.js +20 -34
- package/dist/commonjs/codegen/helpers.js.map +1 -1
- package/dist/commonjs/codegen/index.js +192 -183
- package/dist/commonjs/codegen/index.js.map +1 -1
- package/dist/commonjs/codegen/scope.js +61 -0
- package/dist/commonjs/codegen/scope.js.map +1 -0
- package/dist/commonjs/index.js +5 -12
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/parser/attribute.js +3 -3
- package/dist/commonjs/parser/attribute.js.map +1 -1
- package/dist/commonjs/parser/constants.js +7 -1
- package/dist/commonjs/parser/constants.js.map +1 -1
- package/dist/commonjs/parser/expression.js +2 -5
- package/dist/commonjs/parser/expression.js.map +1 -1
- package/dist/commonjs/parser/html.js +1 -2
- package/dist/commonjs/parser/html.js.map +1 -1
- package/dist/commonjs/parser/index.js +337 -355
- package/dist/commonjs/parser/index.js.map +1 -1
- package/dist/commonjs/parser/parser.js +30 -85
- package/dist/commonjs/parser/parser.js.map +1 -1
- package/dist/commonjs/shared/ir.js +90 -0
- package/dist/commonjs/shared/ir.js.map +1 -0
- package/dist/commonjs/shared/parse5.js +15 -1
- package/dist/commonjs/shared/parse5.js.map +1 -1
- package/dist/commonjs/shared/types.js +7 -1
- package/dist/commonjs/shared/types.js.map +1 -1
- package/dist/types/codegen/codegen.d.ts +3 -25
- package/dist/types/codegen/helpers.d.ts +5 -11
- package/dist/types/codegen/index.d.ts +2 -2
- package/dist/types/codegen/scope.d.ts +8 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/parser/attribute.d.ts +7 -7
- package/dist/types/parser/constants.d.ts +6 -0
- package/dist/types/parser/expression.d.ts +4 -3
- package/dist/types/parser/parser.d.ts +28 -57
- package/dist/types/shared/estree.d.ts +2 -0
- package/dist/types/shared/ir.d.ts +15 -0
- package/dist/types/shared/parse5.d.ts +1 -0
- package/dist/types/shared/types.d.ts +86 -129
- package/package.json +5 -5
- package/dist/commonjs/shared/ast.js +0 -303
- package/dist/commonjs/shared/ast.js.map +0 -1
- package/dist/types/shared/ast.d.ts +0 -45
|
@@ -22,13 +22,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
22
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
const shared_1 = require("@lwc/shared");
|
|
25
26
|
const errors_1 = require("@lwc/errors");
|
|
26
27
|
const html_1 = require("./html");
|
|
27
28
|
const attribute_1 = require("./attribute");
|
|
28
29
|
const expression_1 = require("./expression");
|
|
29
30
|
const t = __importStar(require("../shared/estree"));
|
|
30
31
|
const parse5Utils = __importStar(require("../shared/parse5"));
|
|
31
|
-
const
|
|
32
|
+
const ir_1 = require("../shared/ir");
|
|
32
33
|
const types_1 = require("../shared/types");
|
|
33
34
|
const parser_1 = __importDefault(require("./parser"));
|
|
34
35
|
const constants_1 = require("./constants");
|
|
@@ -64,62 +65,40 @@ function parse(source, state) {
|
|
|
64
65
|
}
|
|
65
66
|
const root = ctx.withErrorRecovery(() => {
|
|
66
67
|
const templateRoot = getTemplateRoot(ctx, fragment);
|
|
67
|
-
return
|
|
68
|
+
return parseElement(ctx, templateRoot);
|
|
68
69
|
});
|
|
69
70
|
return { root, warnings: ctx.warnings };
|
|
70
71
|
}
|
|
71
72
|
exports.default = parse;
|
|
72
|
-
function
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
ctx
|
|
87
|
-
|
|
88
|
-
parseChildren(ctx, parse5Elm,
|
|
89
|
-
|
|
73
|
+
function parseElement(ctx, parse5Elm, parentIRElement) {
|
|
74
|
+
const location = parseElementLocation(ctx, parse5Elm, parentIRElement);
|
|
75
|
+
const element = (0, ir_1.createElement)(parse5Elm, location);
|
|
76
|
+
const parsedAttr = parseAttributes(ctx, element, parse5Elm);
|
|
77
|
+
applyForEach(ctx, element, parsedAttr);
|
|
78
|
+
applyIterator(ctx, element, parsedAttr);
|
|
79
|
+
applyIf(ctx, element, parsedAttr);
|
|
80
|
+
applyHandlers(ctx, element, parsedAttr);
|
|
81
|
+
applyComponent(element);
|
|
82
|
+
applySlot(ctx, element, parsedAttr);
|
|
83
|
+
applyKey(ctx, element, parsedAttr);
|
|
84
|
+
applyLwcDirectives(ctx, element, parsedAttr);
|
|
85
|
+
applyAttributes(ctx, element, parsedAttr);
|
|
86
|
+
validateElement(ctx, element, parse5Elm);
|
|
87
|
+
validateAttributes(ctx, element, parsedAttr);
|
|
88
|
+
validateProperties(ctx, element);
|
|
89
|
+
parseChildren(ctx, parse5Elm, element);
|
|
90
|
+
validateChildren(ctx, element);
|
|
91
|
+
return element;
|
|
90
92
|
}
|
|
91
|
-
function
|
|
92
|
-
const parse5ElmLocation = parseElementLocation(ctx, parse5Elm, parse5ParentLocation);
|
|
93
|
-
const parsedAttr = parseAttributes(ctx, parse5Elm, parse5ElmLocation);
|
|
94
|
-
const directive = parseElementDirectives(ctx, parsedAttr, parentNode, parse5ElmLocation);
|
|
95
|
-
const element = parseBaseElement(ctx, parsedAttr, parse5Elm, directive || parentNode, parse5ElmLocation);
|
|
96
|
-
if (element) {
|
|
97
|
-
applyHandlers(ctx, parsedAttr, element);
|
|
98
|
-
applyKey(ctx, parsedAttr, element, parentNode);
|
|
99
|
-
applyLwcDirectives(ctx, parsedAttr, element);
|
|
100
|
-
applyAttributes(ctx, parsedAttr, element);
|
|
101
|
-
validateElement(ctx, element, parse5Elm);
|
|
102
|
-
validateAttributes(ctx, parsedAttr, element);
|
|
103
|
-
validateProperties(ctx, element);
|
|
104
|
-
}
|
|
105
|
-
validateTemplate(ctx, parsedAttr, parse5Elm, parse5ElmLocation);
|
|
106
|
-
const currentNode = element || directive;
|
|
107
|
-
if (currentNode) {
|
|
108
|
-
parseChildren(ctx, parse5Elm, currentNode, parse5ElmLocation);
|
|
109
|
-
validateChildren(ctx, element);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
function parseElementLocation(ctx, parse5Elm, parse5ParentLocation) {
|
|
93
|
+
function parseElementLocation(ctx, parse5Elm, parentIRElement) {
|
|
113
94
|
var _a;
|
|
114
95
|
let location = parse5Elm.sourceCodeLocation;
|
|
115
|
-
// AST hierarchy is ForBlock > If > BaseElement, if immediate parent is not a BaseElement it is a template.
|
|
116
|
-
const parentNode = ctx.findAncestor(ast.isBaseElement, () => false);
|
|
117
96
|
if (!location) {
|
|
118
97
|
// Parse5 will recover from invalid HTML. When this happens the element's sourceCodeLocation will be undefined.
|
|
119
98
|
// https://github.com/inikulin/parse5/blob/master/packages/parse5/docs/options/parser-options.md#sourcecodelocationinfo
|
|
120
99
|
ctx.warn(errors_1.ParserDiagnostics.INVALID_HTML_RECOVERY, [
|
|
121
100
|
parse5Elm.tagName,
|
|
122
|
-
|
|
101
|
+
parentIRElement === null || parentIRElement === void 0 ? void 0 : parentIRElement.tag,
|
|
123
102
|
]);
|
|
124
103
|
}
|
|
125
104
|
// With parse5 automatically recovering from invalid HTML, some AST nodes might not have
|
|
@@ -131,65 +110,33 @@ function parseElementLocation(ctx, parse5Elm, parse5ParentLocation) {
|
|
|
131
110
|
current = current.parentNode;
|
|
132
111
|
location = current.sourceCodeLocation;
|
|
133
112
|
}
|
|
134
|
-
|
|
113
|
+
// Parent's location is used as the fallback in case the current node's location cannot be found.
|
|
114
|
+
// If there is no parent, use an empty parse5.ElementLocation instead.
|
|
115
|
+
return (_a = location !== null && location !== void 0 ? location : parentIRElement === null || parentIRElement === void 0 ? void 0 : parentIRElement.location) !== null && _a !== void 0 ? _a : parse5Utils.createEmptyElementLocation();
|
|
135
116
|
}
|
|
136
|
-
function
|
|
137
|
-
let current;
|
|
138
|
-
const parsers = [parseForEach, parseForOf, parseIf];
|
|
139
|
-
for (const parser of parsers) {
|
|
140
|
-
const prev = current || parent;
|
|
141
|
-
const node = parser(ctx, parsedAttr, parse5ElmLocation);
|
|
142
|
-
if (node) {
|
|
143
|
-
ctx.addNodeCurrentScope(node);
|
|
144
|
-
prev.children.push(node);
|
|
145
|
-
current = node;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return current;
|
|
149
|
-
}
|
|
150
|
-
function parseBaseElement(ctx, parsedAttr, parse5Elm, parent, parse5ElmLocation) {
|
|
151
|
-
const { tagName: tag } = parse5Elm;
|
|
152
|
-
let element;
|
|
153
|
-
if (tag === 'slot') {
|
|
154
|
-
element = parseSlot(ctx, parsedAttr, parse5ElmLocation);
|
|
155
|
-
// Skip creating template nodes
|
|
156
|
-
}
|
|
157
|
-
else if (tag !== 'template') {
|
|
158
|
-
// Check if the element tag is a valid custom element name and is not part of known standard
|
|
159
|
-
// element name containing a dash.
|
|
160
|
-
if (!tag.includes('-') || constants_1.DASHED_TAGNAME_ELEMENT_SET.has(tag)) {
|
|
161
|
-
element = ast.element(parse5Elm, parse5ElmLocation);
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
element = ast.component(parse5Elm, parse5ElmLocation);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (element) {
|
|
168
|
-
ctx.addNodeCurrentScope(element);
|
|
169
|
-
parent.children.push(element);
|
|
170
|
-
}
|
|
171
|
-
return element;
|
|
172
|
-
}
|
|
173
|
-
function parseChildren(ctx, parse5Parent, parent, parse5ParentLocation) {
|
|
117
|
+
function parseChildren(ctx, parse5Parent, parentIRElement) {
|
|
174
118
|
var _a;
|
|
119
|
+
const parsedChildren = [];
|
|
175
120
|
const children = ((_a = parse5Utils.getTemplateContent(parse5Parent)) !== null && _a !== void 0 ? _a : parse5Parent).childNodes;
|
|
121
|
+
ctx.parentStack.push(parentIRElement);
|
|
176
122
|
for (const child of children) {
|
|
177
123
|
ctx.withErrorRecovery(() => {
|
|
178
124
|
if (parse5Utils.isElementNode(child)) {
|
|
179
|
-
ctx
|
|
180
|
-
|
|
181
|
-
ctx.endScope();
|
|
125
|
+
const elmNode = parseElement(ctx, child, parentIRElement);
|
|
126
|
+
parsedChildren.push(elmNode);
|
|
182
127
|
}
|
|
183
128
|
else if (parse5Utils.isTextNode(child)) {
|
|
184
129
|
const textNodes = parseText(ctx, child);
|
|
185
|
-
|
|
130
|
+
parsedChildren.push(...textNodes);
|
|
186
131
|
}
|
|
187
132
|
else if (parse5Utils.isCommentNode(child)) {
|
|
188
133
|
const commentNode = parseComment(child);
|
|
189
|
-
|
|
134
|
+
parsedChildren.push(commentNode);
|
|
190
135
|
}
|
|
191
136
|
});
|
|
192
137
|
}
|
|
138
|
+
ctx.parentStack.pop();
|
|
139
|
+
parentIRElement.children = parsedChildren;
|
|
193
140
|
}
|
|
194
141
|
function parseText(ctx, parse5Text) {
|
|
195
142
|
const parsedTextNodes = [];
|
|
@@ -198,7 +145,7 @@ function parseText(ctx, parse5Text) {
|
|
|
198
145
|
// Parse5 will recover from invalid HTML. When this happens the node's sourceCodeLocation will be undefined.
|
|
199
146
|
// https://github.com/inikulin/parse5/blob/master/packages/parse5/docs/options/parser-options.md#sourcecodelocationinfo
|
|
200
147
|
// This is a defensive check as this should never happen for TextNode.
|
|
201
|
-
throw new Error(
|
|
148
|
+
throw new Error(`An internal parsing error occurred during node creation; a text node was found without a sourceCodeLocation.`);
|
|
202
149
|
}
|
|
203
150
|
// Extract the raw source to avoid HTML entity decoding done by parse5
|
|
204
151
|
const rawText = (0, html_1.cleanTextNode)(ctx.getSource(location.startOffset, location.endOffset));
|
|
@@ -214,12 +161,12 @@ function parseText(ctx, parse5Text) {
|
|
|
214
161
|
}
|
|
215
162
|
let value;
|
|
216
163
|
if ((0, expression_1.isExpression)(token)) {
|
|
217
|
-
value = (0, expression_1.parseExpression)(ctx, token,
|
|
164
|
+
value = (0, expression_1.parseExpression)(ctx, token, location);
|
|
218
165
|
}
|
|
219
166
|
else {
|
|
220
|
-
value =
|
|
167
|
+
value = (0, html_1.decodeTextContent)(token);
|
|
221
168
|
}
|
|
222
|
-
parsedTextNodes.push(
|
|
169
|
+
parsedTextNodes.push((0, ir_1.createText)(value, location));
|
|
223
170
|
}
|
|
224
171
|
return parsedTextNodes;
|
|
225
172
|
}
|
|
@@ -229,9 +176,9 @@ function parseComment(parse5Comment) {
|
|
|
229
176
|
// Parse5 will recover from invalid HTML. When this happens the node's sourceCodeLocation will be undefined.
|
|
230
177
|
// https://github.com/inikulin/parse5/blob/master/packages/parse5/docs/options/parser-options.md#sourcecodelocationinfo
|
|
231
178
|
// This is a defensive check as this should never happen for CommentNode.
|
|
232
|
-
throw new Error(
|
|
179
|
+
throw new Error(`An internal parsing error occurred during node creation; a comment node was found without a sourceCodeLocation.`);
|
|
233
180
|
}
|
|
234
|
-
return
|
|
181
|
+
return (0, ir_1.createComment)((0, html_1.decodeTextContent)(parse5Comment.data), location);
|
|
235
182
|
}
|
|
236
183
|
function getTemplateRoot(ctx, documentFragment) {
|
|
237
184
|
// Filter all the empty text nodes
|
|
@@ -239,7 +186,7 @@ function getTemplateRoot(ctx, documentFragment) {
|
|
|
239
186
|
(parse5Utils.isTextNode(child) && child.value.trim().length));
|
|
240
187
|
if (validRoots.length > 1) {
|
|
241
188
|
const duplicateRoot = validRoots[1].sourceCodeLocation;
|
|
242
|
-
ctx.
|
|
189
|
+
ctx.throwAtLocation(errors_1.ParserDiagnostics.MULTIPLE_ROOTS_FOUND, duplicateRoot);
|
|
243
190
|
}
|
|
244
191
|
const [root] = validRoots;
|
|
245
192
|
if (!root || !parse5Utils.isElementNode(root)) {
|
|
@@ -247,254 +194,266 @@ function getTemplateRoot(ctx, documentFragment) {
|
|
|
247
194
|
}
|
|
248
195
|
return root;
|
|
249
196
|
}
|
|
250
|
-
function applyHandlers(ctx,
|
|
197
|
+
function applyHandlers(ctx, element, parsedAttr) {
|
|
251
198
|
let eventHandlerAttribute;
|
|
252
199
|
while ((eventHandlerAttribute = parsedAttr.pick(constants_1.EVENT_HANDLER_RE))) {
|
|
253
200
|
const { name } = eventHandlerAttribute;
|
|
254
|
-
if (!
|
|
255
|
-
ctx.
|
|
201
|
+
if (!(0, ir_1.isIRExpressionAttribute)(eventHandlerAttribute)) {
|
|
202
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.EVENT_HANDLER_SHOULD_BE_EXPRESSION, eventHandlerAttribute);
|
|
256
203
|
}
|
|
257
204
|
if (!name.match(constants_1.EVENT_HANDLER_NAME_RE)) {
|
|
258
|
-
ctx.
|
|
205
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.INVALID_EVENT_NAME, eventHandlerAttribute, [name]);
|
|
206
|
+
}
|
|
207
|
+
// Light DOM slots cannot have events because there's no actual `<slot>` element
|
|
208
|
+
if (element.tag === 'slot' && ctx.getRenderMode(element) === types_1.LWCDirectiveRenderMode.light) {
|
|
209
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_LIGHT_SLOT_INVALID_EVENT_LISTENER, element, [
|
|
210
|
+
name,
|
|
211
|
+
]);
|
|
259
212
|
}
|
|
260
213
|
// Strip the `on` prefix from the event handler name
|
|
261
214
|
const eventName = name.slice(2);
|
|
262
|
-
const
|
|
263
|
-
|
|
215
|
+
const on = element.on || (element.on = {});
|
|
216
|
+
on[eventName] = eventHandlerAttribute.value;
|
|
264
217
|
}
|
|
265
218
|
}
|
|
266
|
-
function
|
|
219
|
+
function applyIf(ctx, element, parsedAttr) {
|
|
267
220
|
const ifAttribute = parsedAttr.pick(constants_1.IF_RE);
|
|
268
221
|
if (!ifAttribute) {
|
|
269
222
|
return;
|
|
270
223
|
}
|
|
271
|
-
if (!
|
|
272
|
-
ctx.
|
|
224
|
+
if (!(0, ir_1.isIRExpressionAttribute)(ifAttribute)) {
|
|
225
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.IF_DIRECTIVE_SHOULD_BE_EXPRESSION, ifAttribute);
|
|
273
226
|
}
|
|
274
227
|
const [, modifier] = ifAttribute.name.split(':');
|
|
275
228
|
if (!constants_1.VALID_IF_MODIFIER.has(modifier)) {
|
|
276
|
-
ctx.
|
|
229
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.UNEXPECTED_IF_MODIFIER, ifAttribute, [modifier]);
|
|
277
230
|
}
|
|
278
|
-
|
|
231
|
+
element.if = ifAttribute.value;
|
|
232
|
+
element.ifModifier = modifier;
|
|
279
233
|
}
|
|
280
|
-
function
|
|
234
|
+
function applyLwcDirectives(ctx, element, parsedAttr) {
|
|
281
235
|
const lwcAttribute = parsedAttr.get(constants_1.LWC_RE);
|
|
282
236
|
if (!lwcAttribute) {
|
|
283
237
|
return;
|
|
284
238
|
}
|
|
285
|
-
|
|
286
|
-
|
|
239
|
+
if (!constants_1.LWC_DIRECTIVE_SET.has(lwcAttribute.name) &&
|
|
240
|
+
!constants_1.ROOT_TEMPLATE_DIRECTIVES_SET.has(lwcAttribute.name)) {
|
|
241
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.UNKNOWN_LWC_DIRECTIVE, element, [
|
|
242
|
+
lwcAttribute.name,
|
|
243
|
+
`<${element.tag}>`,
|
|
244
|
+
]);
|
|
245
|
+
}
|
|
246
|
+
const lwcOpts = {};
|
|
247
|
+
applyLwcDynamicDirective(ctx, element, parsedAttr, lwcOpts);
|
|
248
|
+
applyLwcDomDirective(ctx, element, parsedAttr, lwcOpts);
|
|
249
|
+
applyLwcRenderModeDirective(ctx, element, parsedAttr, lwcOpts);
|
|
250
|
+
applyLwcPreserveCommentsDirective(ctx, element, parsedAttr, lwcOpts);
|
|
251
|
+
applyLwcInnerHtmlDirective(ctx, element, parsedAttr, lwcOpts);
|
|
252
|
+
element.lwc = lwcOpts;
|
|
287
253
|
}
|
|
288
|
-
function applyLwcRenderModeDirective(ctx, parsedAttr,
|
|
289
|
-
const lwcRenderModeAttribute = parsedAttr.
|
|
254
|
+
function applyLwcRenderModeDirective(ctx, element, parsedAttr, lwcOpts) {
|
|
255
|
+
const lwcRenderModeAttribute = parsedAttr.get(constants_1.ROOT_TEMPLATE_DIRECTIVES.RENDER_MODE);
|
|
290
256
|
if (!lwcRenderModeAttribute) {
|
|
291
257
|
return;
|
|
292
258
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
(
|
|
296
|
-
renderDomAttr.value !== types_1.LWCDirectiveRenderMode.light)) {
|
|
297
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_RENDER_MODE_INVALID_VALUE, root);
|
|
259
|
+
if (!(0, ir_1.isIRStringAttribute)(lwcRenderModeAttribute) ||
|
|
260
|
+
(lwcRenderModeAttribute.value !== 'shadow' && lwcRenderModeAttribute.value !== 'light')) {
|
|
261
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_RENDER_MODE_INVALID_VALUE, element);
|
|
298
262
|
}
|
|
299
|
-
|
|
263
|
+
if (ctx.parentStack.length > 0) {
|
|
264
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.UNKNOWN_LWC_DIRECTIVE, element, [
|
|
265
|
+
constants_1.ROOT_TEMPLATE_DIRECTIVES.RENDER_MODE,
|
|
266
|
+
`<${element.tag}>`,
|
|
267
|
+
]);
|
|
268
|
+
}
|
|
269
|
+
lwcOpts.renderMode = lwcRenderModeAttribute.value;
|
|
300
270
|
}
|
|
301
|
-
function applyLwcPreserveCommentsDirective(ctx, parsedAttr,
|
|
302
|
-
const lwcPreserveCommentAttribute = parsedAttr.
|
|
271
|
+
function applyLwcPreserveCommentsDirective(ctx, element, parsedAttr, lwcOpts) {
|
|
272
|
+
const lwcPreserveCommentAttribute = parsedAttr.get(constants_1.ROOT_TEMPLATE_DIRECTIVES.PRESERVE_COMMENTS);
|
|
303
273
|
if (!lwcPreserveCommentAttribute) {
|
|
304
274
|
return;
|
|
305
275
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
276
|
+
if (ctx.parentStack.length) {
|
|
277
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.UNKNOWN_LWC_DIRECTIVE, element, [
|
|
278
|
+
constants_1.ROOT_TEMPLATE_DIRECTIVES.PRESERVE_COMMENTS,
|
|
279
|
+
`<${element.tag}>`,
|
|
280
|
+
]);
|
|
281
|
+
}
|
|
282
|
+
else if (!(0, ir_1.isIRBooleanAttribute)(lwcPreserveCommentAttribute)) {
|
|
283
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.PRESERVE_COMMENTS_MUST_BE_BOOLEAN, element);
|
|
309
284
|
}
|
|
310
|
-
|
|
285
|
+
lwcOpts.preserveComments = lwcPreserveCommentAttribute;
|
|
311
286
|
}
|
|
312
|
-
function
|
|
313
|
-
const
|
|
314
|
-
if (!
|
|
287
|
+
function applyLwcInnerHtmlDirective(ctx, element, parsedAttr, lwcOpts) {
|
|
288
|
+
const lwcInnerHtmlDirective = parsedAttr.pick(constants_1.LWC_DIRECTIVES.INNER_HTML);
|
|
289
|
+
if (!lwcInnerHtmlDirective) {
|
|
315
290
|
return;
|
|
316
291
|
}
|
|
317
|
-
if (
|
|
318
|
-
ctx.
|
|
319
|
-
|
|
320
|
-
`<${element.name}>`,
|
|
292
|
+
if ((0, ir_1.isCustomElement)(element)) {
|
|
293
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_CUSTOM_ELEMENT, element, [
|
|
294
|
+
`<${element.tag}>`,
|
|
321
295
|
]);
|
|
322
296
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
constants_1.ROOT_TEMPLATE_DIRECTIVES.RENDER_MODE,
|
|
327
|
-
`<${element.name}>`,
|
|
297
|
+
if (element.tag === 'slot' || element.tag === 'template') {
|
|
298
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_ELEMENT, element, [
|
|
299
|
+
`<${element.tag}>`,
|
|
328
300
|
]);
|
|
329
301
|
}
|
|
330
|
-
if (
|
|
331
|
-
ctx.
|
|
332
|
-
|
|
333
|
-
`<${element.name}>`,
|
|
302
|
+
if (lwcInnerHtmlDirective.type === types_1.IRAttributeType.Boolean) {
|
|
303
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_VALUE, element, [
|
|
304
|
+
`<${element.tag}>`,
|
|
334
305
|
]);
|
|
335
306
|
}
|
|
336
|
-
|
|
337
|
-
applyLwcDomDirective(ctx, parsedAttr, element);
|
|
338
|
-
applyLwcInnerHtmlDirective(ctx, parsedAttr, element);
|
|
307
|
+
lwcOpts.innerHTML = lwcInnerHtmlDirective.value;
|
|
339
308
|
}
|
|
340
|
-
function applyLwcDynamicDirective(ctx, parsedAttr,
|
|
341
|
-
const {
|
|
342
|
-
const lwcDynamicAttribute = parsedAttr.pick(
|
|
309
|
+
function applyLwcDynamicDirective(ctx, element, parsedAttr, lwcOpts) {
|
|
310
|
+
const { tag } = element;
|
|
311
|
+
const lwcDynamicAttribute = parsedAttr.pick(constants_1.LWC_DIRECTIVES.DYNAMIC);
|
|
343
312
|
if (!lwcDynamicAttribute) {
|
|
344
313
|
return;
|
|
345
314
|
}
|
|
346
315
|
if (!ctx.config.experimentalDynamicDirective) {
|
|
347
|
-
ctx.
|
|
316
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.INVALID_OPTS_LWC_DYNAMIC, element);
|
|
348
317
|
}
|
|
349
|
-
if (!
|
|
350
|
-
ctx.
|
|
318
|
+
if (!(0, ir_1.isCustomElement)(element)) {
|
|
319
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.INVALID_LWC_DYNAMIC_ON_NATIVE_ELEMENT, element, [
|
|
351
320
|
`<${tag}>`,
|
|
352
321
|
]);
|
|
353
322
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
323
|
+
if (!(0, ir_1.isIRExpressionAttribute)(lwcDynamicAttribute)) {
|
|
324
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.INVALID_LWC_DYNAMIC_LITERAL_PROP, element, [
|
|
325
|
+
`<${tag}>`,
|
|
326
|
+
]);
|
|
357
327
|
}
|
|
358
|
-
|
|
328
|
+
lwcOpts.dynamic = lwcDynamicAttribute.value;
|
|
359
329
|
}
|
|
360
|
-
function applyLwcDomDirective(ctx, parsedAttr,
|
|
361
|
-
const {
|
|
362
|
-
const lwcDomAttribute = parsedAttr.pick(
|
|
330
|
+
function applyLwcDomDirective(ctx, element, parsedAttr, lwcOpts) {
|
|
331
|
+
const { tag } = element;
|
|
332
|
+
const lwcDomAttribute = parsedAttr.pick(constants_1.LWC_DIRECTIVES.DOM);
|
|
363
333
|
if (!lwcDomAttribute) {
|
|
364
334
|
return;
|
|
365
335
|
}
|
|
366
|
-
if (ctx.
|
|
367
|
-
ctx.
|
|
336
|
+
if (ctx.getRenderMode(element) === types_1.LWCDirectiveRenderMode.light) {
|
|
337
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_IN_LIGHT_DOM, element, [`<${tag}>`]);
|
|
368
338
|
}
|
|
369
|
-
if (
|
|
370
|
-
ctx.
|
|
339
|
+
if ((0, ir_1.isCustomElement)(element)) {
|
|
340
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_CUSTOM_ELEMENT, element, [`<${tag}>`]);
|
|
371
341
|
}
|
|
372
|
-
if (
|
|
373
|
-
ctx.
|
|
342
|
+
if (tag === 'slot') {
|
|
343
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_SLOT_ELEMENT, element);
|
|
374
344
|
}
|
|
375
|
-
|
|
376
|
-
|
|
345
|
+
if (!(0, ir_1.isIRStringAttribute)(lwcDomAttribute) ||
|
|
346
|
+
shared_1.hasOwnProperty.call(types_1.LWCDirectiveDomMode, lwcDomAttribute.value) === false) {
|
|
377
347
|
const possibleValues = Object.keys(types_1.LWCDirectiveDomMode)
|
|
378
348
|
.map((value) => `"${value}"`)
|
|
379
349
|
.join(', or ');
|
|
380
|
-
ctx.
|
|
350
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_VALUE, element, [possibleValues]);
|
|
381
351
|
}
|
|
382
|
-
|
|
352
|
+
lwcOpts.dom = lwcDomAttribute.value;
|
|
383
353
|
}
|
|
384
|
-
function
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
if (ast.isComponent(element)) {
|
|
390
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_CUSTOM_ELEMENT, element, [
|
|
391
|
-
`<${element.name}>`,
|
|
392
|
-
]);
|
|
393
|
-
}
|
|
394
|
-
if (ast.isSlot(element)) {
|
|
395
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_ELEMENT, element, [
|
|
396
|
-
`<${element.name}>`,
|
|
397
|
-
]);
|
|
398
|
-
}
|
|
399
|
-
const { value: innerHTMLVal } = lwcInnerHtmlDirective;
|
|
400
|
-
if (!ast.isStringLiteral(innerHTMLVal) && !ast.isExpression(innerHTMLVal)) {
|
|
401
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_VALUE, element, [
|
|
402
|
-
`<${element.name}>`,
|
|
403
|
-
]);
|
|
404
|
-
}
|
|
405
|
-
element.directives.push(ast.innerHTMLDirective(innerHTMLVal, lwcInnerHtmlDirective.location));
|
|
406
|
-
}
|
|
407
|
-
function parseForEach(ctx, parsedAttr, parse5ElmLocation) {
|
|
408
|
-
const forEachAttribute = parsedAttr.pick('for:each');
|
|
409
|
-
const forItemAttribute = parsedAttr.pick('for:item');
|
|
410
|
-
const forIndex = parsedAttr.pick('for:index');
|
|
354
|
+
function applyForEach(ctx, element, parsedAttr) {
|
|
355
|
+
const forEachAttribute = parsedAttr.pick(constants_1.FOR_DIRECTIVES.FOR_EACH);
|
|
356
|
+
const forItemAttribute = parsedAttr.pick(constants_1.FOR_DIRECTIVES.FOR_ITEM);
|
|
357
|
+
const forIndex = parsedAttr.pick(constants_1.FOR_DIRECTIVES.FOR_INDEX);
|
|
411
358
|
if (forEachAttribute && forItemAttribute) {
|
|
412
|
-
if (!
|
|
413
|
-
ctx.
|
|
359
|
+
if (!(0, ir_1.isIRExpressionAttribute)(forEachAttribute)) {
|
|
360
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.FOR_EACH_DIRECTIVE_SHOULD_BE_EXPRESSION, forEachAttribute);
|
|
414
361
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.FOR_ITEM_DIRECTIVE_SHOULD_BE_STRING, forItemAttribute);
|
|
362
|
+
if (!(0, ir_1.isIRStringAttribute)(forItemAttribute)) {
|
|
363
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.FOR_ITEM_DIRECTIVE_SHOULD_BE_STRING, forItemAttribute);
|
|
418
364
|
}
|
|
419
|
-
const item = (0, expression_1.parseIdentifier)(ctx,
|
|
365
|
+
const item = (0, expression_1.parseIdentifier)(ctx, forItemAttribute.value, forItemAttribute.location);
|
|
420
366
|
let index;
|
|
421
367
|
if (forIndex) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.FOR_INDEX_DIRECTIVE_SHOULD_BE_STRING, forIndex);
|
|
368
|
+
if (!(0, ir_1.isIRStringAttribute)(forIndex)) {
|
|
369
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.FOR_INDEX_DIRECTIVE_SHOULD_BE_STRING, forIndex);
|
|
425
370
|
}
|
|
426
|
-
index = (0, expression_1.parseIdentifier)(ctx,
|
|
371
|
+
index = (0, expression_1.parseIdentifier)(ctx, forIndex.value, forIndex.location);
|
|
427
372
|
}
|
|
428
|
-
|
|
373
|
+
element.forEach = {
|
|
374
|
+
expression: forEachAttribute.value,
|
|
375
|
+
item,
|
|
376
|
+
index,
|
|
377
|
+
};
|
|
429
378
|
}
|
|
430
379
|
else if (forEachAttribute || forItemAttribute) {
|
|
431
|
-
ctx.
|
|
380
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.FOR_EACH_AND_FOR_ITEM_DIRECTIVES_SHOULD_BE_TOGETHER, element);
|
|
432
381
|
}
|
|
433
382
|
}
|
|
434
|
-
function
|
|
383
|
+
function applyIterator(ctx, element, parsedAttr) {
|
|
435
384
|
const iteratorExpression = parsedAttr.pick(constants_1.ITERATOR_RE);
|
|
436
385
|
if (!iteratorExpression) {
|
|
437
386
|
return;
|
|
438
387
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
388
|
+
if (element.forEach) {
|
|
389
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.INVALID_FOR_EACH_WITH_ITERATOR, element, [
|
|
390
|
+
iteratorExpression.name,
|
|
391
|
+
]);
|
|
442
392
|
}
|
|
443
393
|
const iteratorAttributeName = iteratorExpression.name;
|
|
444
394
|
const [, iteratorName] = iteratorAttributeName.split(':');
|
|
445
|
-
if (!
|
|
446
|
-
ctx.
|
|
395
|
+
if (!(0, ir_1.isIRExpressionAttribute)(iteratorExpression)) {
|
|
396
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.DIRECTIVE_SHOULD_BE_EXPRESSION, iteratorExpression, [
|
|
447
397
|
iteratorExpression.name,
|
|
448
398
|
]);
|
|
449
399
|
}
|
|
450
400
|
const iterator = (0, expression_1.parseIdentifier)(ctx, iteratorName, iteratorExpression.location);
|
|
451
|
-
|
|
401
|
+
element.forOf = {
|
|
402
|
+
expression: iteratorExpression.value,
|
|
403
|
+
iterator,
|
|
404
|
+
};
|
|
452
405
|
}
|
|
453
|
-
function applyKey(ctx,
|
|
454
|
-
const {
|
|
406
|
+
function applyKey(ctx, element, parsedAttr) {
|
|
407
|
+
const { tag } = element;
|
|
455
408
|
const keyAttribute = parsedAttr.pick('key');
|
|
456
409
|
if (keyAttribute) {
|
|
457
|
-
if (!
|
|
458
|
-
ctx.
|
|
410
|
+
if (!(0, ir_1.isIRExpressionAttribute)(keyAttribute)) {
|
|
411
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.KEY_ATTRIBUTE_SHOULD_BE_EXPRESSION, keyAttribute);
|
|
459
412
|
}
|
|
460
|
-
const forOfParent = getForOfParent(ctx
|
|
461
|
-
const forEachParent = getForEachParent(ctx);
|
|
413
|
+
const forOfParent = getForOfParent(ctx);
|
|
414
|
+
const forEachParent = getForEachParent(ctx, element);
|
|
462
415
|
if (forOfParent) {
|
|
463
|
-
if (attributeExpressionReferencesForOfIndex(keyAttribute, forOfParent)) {
|
|
464
|
-
ctx.
|
|
416
|
+
if (attributeExpressionReferencesForOfIndex(keyAttribute, forOfParent.forOf)) {
|
|
417
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.KEY_SHOULDNT_REFERENCE_ITERATOR_INDEX, keyAttribute, [tag]);
|
|
465
418
|
}
|
|
466
419
|
}
|
|
467
420
|
else if (forEachParent) {
|
|
468
|
-
if (attributeExpressionReferencesForEachIndex(keyAttribute, forEachParent)) {
|
|
421
|
+
if (attributeExpressionReferencesForEachIndex(keyAttribute, forEachParent.forEach)) {
|
|
469
422
|
const name = 'name' in keyAttribute.value && keyAttribute.value.name;
|
|
470
|
-
ctx.
|
|
423
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.KEY_SHOULDNT_REFERENCE_FOR_EACH_INDEX, keyAttribute, [tag, name]);
|
|
471
424
|
}
|
|
472
425
|
}
|
|
473
|
-
element.
|
|
426
|
+
element.forKey = keyAttribute.value;
|
|
474
427
|
}
|
|
475
|
-
else if (isInIteratorElement(ctx,
|
|
476
|
-
ctx.
|
|
428
|
+
else if (isInIteratorElement(ctx, element) && !(0, ir_1.isTemplate)(element)) {
|
|
429
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.MISSING_KEY_IN_ITERATOR, element, [tag]);
|
|
477
430
|
}
|
|
478
431
|
}
|
|
479
|
-
function
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
432
|
+
function applyComponent(element) {
|
|
433
|
+
const { tag } = element;
|
|
434
|
+
// Check if the element tag is a valid custom element name and is not part of known standard
|
|
435
|
+
// element name containing a dash.
|
|
436
|
+
if (!tag.includes('-') || constants_1.DASHED_TAGNAME_ELEMENT_SET.has(tag)) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
element.component = tag;
|
|
440
|
+
}
|
|
441
|
+
function applySlot(ctx, element, parsedAttr) {
|
|
442
|
+
// Early exit if the element is not a slot
|
|
443
|
+
if (element.tag !== 'slot') {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (element.forEach || element.forOf || element.if) {
|
|
447
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.SLOT_TAG_CANNOT_HAVE_DIRECTIVES, element);
|
|
484
448
|
}
|
|
485
449
|
// Can't handle slots in applySlot because it would be too late for class and style attrs
|
|
486
|
-
if (ctx.
|
|
450
|
+
if (ctx.getRenderMode(element) === types_1.LWCDirectiveRenderMode.light) {
|
|
487
451
|
const invalidAttrs = parsedAttr
|
|
488
452
|
.getAttributes()
|
|
489
453
|
.filter(({ name }) => name !== 'name')
|
|
490
454
|
.map(({ name }) => name);
|
|
491
|
-
if (invalidAttrs.length) {
|
|
492
|
-
|
|
493
|
-
const eventHandler = invalidAttrs.find((name) => name.match(constants_1.EVENT_HANDLER_NAME_RE));
|
|
494
|
-
if (eventHandler) {
|
|
495
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_LIGHT_SLOT_INVALID_EVENT_LISTENER, location, [eventHandler]);
|
|
496
|
-
}
|
|
497
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_LIGHT_SLOT_INVALID_ATTRIBUTES, location, [
|
|
455
|
+
if (invalidAttrs.length > 0) {
|
|
456
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_LIGHT_SLOT_INVALID_ATTRIBUTES, element, [
|
|
498
457
|
invalidAttrs.join(','),
|
|
499
458
|
]);
|
|
500
459
|
}
|
|
@@ -503,57 +462,57 @@ function parseSlot(ctx, parsedAttr, parse5ElmLocation) {
|
|
|
503
462
|
let name = '';
|
|
504
463
|
const nameAttribute = parsedAttr.get('name');
|
|
505
464
|
if (nameAttribute) {
|
|
506
|
-
if (
|
|
507
|
-
ctx.
|
|
465
|
+
if ((0, ir_1.isIRExpressionAttribute)(nameAttribute)) {
|
|
466
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.NAME_ON_SLOT_CANNOT_BE_EXPRESSION, nameAttribute);
|
|
508
467
|
}
|
|
509
|
-
else if (
|
|
510
|
-
name = nameAttribute.value
|
|
468
|
+
else if ((0, ir_1.isIRStringAttribute)(nameAttribute)) {
|
|
469
|
+
name = nameAttribute.value;
|
|
511
470
|
}
|
|
512
471
|
}
|
|
472
|
+
element.slotName = name;
|
|
513
473
|
const alreadySeen = ctx.seenSlots.has(name);
|
|
514
474
|
ctx.seenSlots.add(name);
|
|
515
475
|
if (alreadySeen) {
|
|
516
|
-
ctx.
|
|
476
|
+
return ctx.warnOnIRNode(errors_1.ParserDiagnostics.NO_DUPLICATE_SLOTS, element, [
|
|
517
477
|
name === '' ? 'default' : `name="${name}"`,
|
|
518
478
|
]);
|
|
519
479
|
}
|
|
520
|
-
else if (isInIteration(ctx)) {
|
|
521
|
-
ctx.
|
|
480
|
+
else if (isInIteration(ctx, element)) {
|
|
481
|
+
return ctx.warnOnIRNode(errors_1.ParserDiagnostics.NO_SLOTS_IN_ITERATOR, element, [
|
|
522
482
|
name === '' ? 'default' : `name="${name}"`,
|
|
523
483
|
]);
|
|
524
484
|
}
|
|
525
|
-
return ast.slot(name, parse5ElmLocation);
|
|
526
485
|
}
|
|
527
|
-
function applyAttributes(ctx,
|
|
528
|
-
const {
|
|
486
|
+
function applyAttributes(ctx, element, parsedAttr) {
|
|
487
|
+
const { tag } = element;
|
|
529
488
|
const attributes = parsedAttr.getAttributes();
|
|
530
489
|
for (const attr of attributes) {
|
|
531
490
|
const { name } = attr;
|
|
532
491
|
if (!(0, attribute_1.isValidHTMLAttribute)(tag, name)) {
|
|
533
|
-
ctx.
|
|
492
|
+
ctx.warnOnIRNode(errors_1.ParserDiagnostics.INVALID_HTML_ATTRIBUTE, attr, [name, tag]);
|
|
534
493
|
}
|
|
535
494
|
if (name.match(/[^a-z0-9]$/)) {
|
|
536
|
-
ctx.
|
|
495
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.ATTRIBUTE_NAME_MUST_END_WITH_ALPHA_NUMERIC_CHARACTER, attr, [name, tag]);
|
|
537
496
|
}
|
|
538
497
|
if (!/^-*[a-z]/.test(name)) {
|
|
539
|
-
ctx.
|
|
498
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.ATTRIBUTE_NAME_MUST_START_WITH_ALPHABETIC_OR_HYPHEN_CHARACTER, attr, [name, tag]);
|
|
540
499
|
}
|
|
541
500
|
// disallow attr name which combines underscore character with special character.
|
|
542
501
|
// We normalize camel-cased names with underscores caMel -> ca-mel; thus sanitization.
|
|
543
502
|
if (name.match(/_[^a-z0-9]|[^a-z0-9]_/)) {
|
|
544
|
-
ctx.
|
|
503
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.ATTRIBUTE_NAME_CANNOT_COMBINE_UNDERSCORE_WITH_SPECIAL_CHARS, attr, [name, tag]);
|
|
545
504
|
}
|
|
546
|
-
if (
|
|
505
|
+
if ((0, ir_1.isIRStringAttribute)(attr)) {
|
|
547
506
|
if (name === 'id') {
|
|
548
|
-
const { value } = attr
|
|
507
|
+
const { value } = attr;
|
|
549
508
|
if (/\s+/.test(value)) {
|
|
550
|
-
ctx.
|
|
509
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.INVALID_ID_ATTRIBUTE, attr, [value]);
|
|
551
510
|
}
|
|
552
|
-
if (isInIteration(ctx)) {
|
|
553
|
-
ctx.
|
|
511
|
+
if (isInIteration(ctx, element)) {
|
|
512
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.INVALID_STATIC_ID_IN_ITERATION, attr);
|
|
554
513
|
}
|
|
555
514
|
if (ctx.seenIds.has(value)) {
|
|
556
|
-
ctx.
|
|
515
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.DUPLICATE_ID_FOUND, attr, [value]);
|
|
557
516
|
}
|
|
558
517
|
else {
|
|
559
518
|
ctx.seenIds.add(value);
|
|
@@ -561,162 +520,162 @@ function applyAttributes(ctx, parsedAttr, element) {
|
|
|
561
520
|
}
|
|
562
521
|
}
|
|
563
522
|
// Prevent usage of the slot attribute with expression.
|
|
564
|
-
if (name === 'slot' &&
|
|
565
|
-
ctx.
|
|
523
|
+
if (name === 'slot' && (0, ir_1.isIRExpressionAttribute)(attr)) {
|
|
524
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.SLOT_ATTRIBUTE_CANNOT_BE_EXPRESSION, attr);
|
|
566
525
|
}
|
|
567
526
|
// the if branch handles
|
|
568
527
|
// 1. All attributes for standard elements except 1 case are handled as attributes
|
|
569
528
|
// 2. For custom elements, only key, slot and data are handled as attributes, rest as properties
|
|
570
529
|
if ((0, attribute_1.isAttribute)(element, name)) {
|
|
571
|
-
element.
|
|
530
|
+
const attrs = element.attrs || (element.attrs = {});
|
|
531
|
+
attrs[name] = attr;
|
|
572
532
|
}
|
|
573
533
|
else {
|
|
574
|
-
const
|
|
575
|
-
|
|
534
|
+
const props = element.props || (element.props = {});
|
|
535
|
+
props[(0, attribute_1.attributeToPropertyName)(name)] = attr;
|
|
576
536
|
parsedAttr.pick(name);
|
|
577
537
|
}
|
|
578
538
|
}
|
|
579
539
|
}
|
|
580
|
-
function
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
540
|
+
function validateElement(ctx, element, node) {
|
|
541
|
+
const { tag, namespace, location } = element;
|
|
542
|
+
const isRoot = ctx.parentStack.length === 0;
|
|
543
|
+
if (isRoot) {
|
|
544
|
+
if (!(0, ir_1.isTemplate)(element)) {
|
|
545
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.ROOT_TAG_SHOULD_BE_TEMPLATE, element, [tag]);
|
|
546
|
+
}
|
|
547
|
+
const rootHasUnknownAttributes = node.attrs.some(({ name }) => !constants_1.ROOT_TEMPLATE_DIRECTIVES_SET.has(name));
|
|
548
|
+
if (rootHasUnknownAttributes) {
|
|
549
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.ROOT_TEMPLATE_HAS_UNKNOWN_ATTRIBUTES, element);
|
|
550
|
+
}
|
|
586
551
|
}
|
|
587
|
-
}
|
|
588
|
-
function validateElement(ctx, element, parse5Elm) {
|
|
589
|
-
const { tagName: tag, namespaceURI: namespace } = parse5Elm;
|
|
590
552
|
// Check if a non-void element has a matching closing tag.
|
|
591
553
|
//
|
|
592
554
|
// Note: Parse5 currently fails to collect end tag location for element with a tag name
|
|
593
555
|
// containing an upper case character (inikulin/parse5#352).
|
|
594
|
-
const hasClosingTag = Boolean(
|
|
556
|
+
const hasClosingTag = Boolean(location.endTag);
|
|
595
557
|
const isVoidElement = constants_1.VOID_ELEMENT_SET.has(tag);
|
|
596
558
|
if (!isVoidElement && !hasClosingTag && tag === tag.toLocaleLowerCase()) {
|
|
597
|
-
ctx.
|
|
559
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.NO_MATCHING_CLOSING_TAGS, element, [tag]);
|
|
598
560
|
}
|
|
599
561
|
if (tag === 'style' && namespace === constants_1.HTML_NAMESPACE_URI) {
|
|
600
|
-
ctx.
|
|
562
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.STYLE_TAG_NOT_ALLOWED_IN_TEMPLATE, element);
|
|
563
|
+
}
|
|
564
|
+
else if ((0, ir_1.isTemplate)(element)) {
|
|
565
|
+
// We check if the template element has some modifier applied to it. Directly checking if one of the
|
|
566
|
+
// IRElement property is impossible. For example when an error occurs during the parsing of the if
|
|
567
|
+
// expression, the `element.if` property remains undefined. It would results in 2 warnings instead of 1:
|
|
568
|
+
// - Invalid if expression
|
|
569
|
+
// - Unexpected template element
|
|
570
|
+
//
|
|
571
|
+
// Checking if the original HTMLElement has some attributes applied is a good enough for now.
|
|
572
|
+
if (!isRoot) {
|
|
573
|
+
if (!node.attrs.length) {
|
|
574
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.NO_DIRECTIVE_FOUND_ON_TEMPLATE, element);
|
|
575
|
+
}
|
|
576
|
+
// Non root templates only support for:each, iterator and if directives
|
|
577
|
+
if (element.on || element.attrs || element.props || element.forKey || element.lwc) {
|
|
578
|
+
ctx.warnOnIRNode(errors_1.ParserDiagnostics.UNKNOWN_TEMPLATE_ATTRIBUTE, element);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
601
581
|
}
|
|
602
582
|
else {
|
|
603
583
|
const isNotAllowedHtmlTag = constants_1.DISALLOWED_HTML_TAGS.has(tag);
|
|
604
584
|
if (namespace === constants_1.HTML_NAMESPACE_URI && isNotAllowedHtmlTag) {
|
|
605
|
-
ctx.
|
|
585
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.FORBIDDEN_TAG_ON_TEMPLATE, element, [tag]);
|
|
606
586
|
}
|
|
607
587
|
const isNotAllowedSvgTag = !constants_1.SUPPORTED_SVG_TAGS.has(tag);
|
|
608
588
|
if (namespace === constants_1.SVG_NAMESPACE_URI && isNotAllowedSvgTag) {
|
|
609
|
-
ctx.
|
|
589
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.FORBIDDEN_SVG_NAMESPACE_IN_TEMPLATE, element, [
|
|
590
|
+
tag,
|
|
591
|
+
]);
|
|
610
592
|
}
|
|
611
593
|
const isNotAllowedMathMlTag = constants_1.DISALLOWED_MATHML_TAGS.has(tag);
|
|
612
594
|
if (namespace === constants_1.MATHML_NAMESPACE_URI && isNotAllowedMathMlTag) {
|
|
613
|
-
ctx.
|
|
595
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.FORBIDDEN_MATHML_NAMESPACE_IN_TEMPLATE, element, [
|
|
614
596
|
tag,
|
|
615
597
|
]);
|
|
616
598
|
}
|
|
617
|
-
const isKnownTag =
|
|
599
|
+
const isKnownTag = (0, ir_1.isCustomElement)(element) ||
|
|
618
600
|
constants_1.KNOWN_HTML_ELEMENTS.has(tag) ||
|
|
619
601
|
constants_1.SUPPORTED_SVG_TAGS.has(tag) ||
|
|
620
602
|
constants_1.DASHED_TAGNAME_ELEMENT_SET.has(tag);
|
|
621
603
|
if (!isKnownTag) {
|
|
622
|
-
ctx.
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
function validateTemplate(ctx, parsedAttr, parse5Elm, parse5ElmLocation) {
|
|
627
|
-
if (parse5Elm.tagName === 'template') {
|
|
628
|
-
const location = ast.sourceLocation(parse5ElmLocation);
|
|
629
|
-
// Empty templates not allowed outside of root
|
|
630
|
-
if (!parse5Elm.attrs.length) {
|
|
631
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.NO_DIRECTIVE_FOUND_ON_TEMPLATE, location);
|
|
632
|
-
}
|
|
633
|
-
if (parsedAttr.get(constants_1.LWC_DIRECTIVES.INNER_HTML)) {
|
|
634
|
-
ctx.throwAtLocation(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_ELEMENT, location, [
|
|
635
|
-
'<template>',
|
|
636
|
-
]);
|
|
637
|
-
}
|
|
638
|
-
// Non root templates only support for:each, iterator and if directives
|
|
639
|
-
if (parsedAttr.getAttributes().length) {
|
|
640
|
-
ctx.warnAtLocation(errors_1.ParserDiagnostics.UNKNOWN_TEMPLATE_ATTRIBUTE, location);
|
|
604
|
+
ctx.warnOnIRNode(errors_1.ParserDiagnostics.UNKNOWN_HTML_TAG_IN_TEMPLATE, element, [tag]);
|
|
641
605
|
}
|
|
642
606
|
}
|
|
643
607
|
}
|
|
644
608
|
function validateChildren(ctx, element) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
const effectiveChildren = ctx.preserveComments
|
|
609
|
+
var _a, _b;
|
|
610
|
+
const effectiveChildren = ctx.getPreserveComments(element)
|
|
649
611
|
? element.children
|
|
650
|
-
: element.children.filter((child) =>
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
ctx.throwOnNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_CONTENTS, element);
|
|
612
|
+
: element.children.filter((child) => child.type !== 'comment');
|
|
613
|
+
if (((_a = element.lwc) === null || _a === void 0 ? void 0 : _a.dom) && effectiveChildren.length > 0) {
|
|
614
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_DOM_INVALID_CONTENTS, element);
|
|
654
615
|
}
|
|
655
616
|
// prevents lwc:inner-html to be used in an element with content
|
|
656
|
-
if (element.
|
|
657
|
-
ctx.
|
|
658
|
-
`<${element.
|
|
617
|
+
if (((_b = element.lwc) === null || _b === void 0 ? void 0 : _b.innerHTML) && effectiveChildren.length > 0) {
|
|
618
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.LWC_INNER_HTML_INVALID_CONTENTS, element, [
|
|
619
|
+
`<${element.tag}>`,
|
|
659
620
|
]);
|
|
660
621
|
}
|
|
661
622
|
}
|
|
662
|
-
function validateAttributes(ctx,
|
|
663
|
-
const {
|
|
623
|
+
function validateAttributes(ctx, element, parsedAttr) {
|
|
624
|
+
const { tag } = element;
|
|
664
625
|
const attributes = parsedAttr.getAttributes();
|
|
665
626
|
for (const attr of attributes) {
|
|
666
|
-
const { name: attrName
|
|
627
|
+
const { name: attrName } = attr;
|
|
667
628
|
if ((0, attribute_1.isProhibitedIsAttribute)(attrName)) {
|
|
668
|
-
ctx.
|
|
629
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.IS_ATTRIBUTE_NOT_SUPPORTED, element);
|
|
669
630
|
}
|
|
670
631
|
if ((0, attribute_1.isTabIndexAttribute)(attrName)) {
|
|
671
|
-
if (!
|
|
672
|
-
ctx.
|
|
632
|
+
if (!(0, ir_1.isIRExpressionAttribute)(attr) && !(0, attribute_1.isValidTabIndexAttributeValue)(attr.value)) {
|
|
633
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.INVALID_TABINDEX_ATTRIBUTE, element);
|
|
673
634
|
}
|
|
674
635
|
}
|
|
675
636
|
// TODO [#1136]: once the template compiler emits the element namespace information to the engine we should
|
|
676
637
|
// restrict the validation of the "srcdoc" attribute on the "iframe" element only if this element is
|
|
677
638
|
// part of the HTML namespace.
|
|
678
639
|
if (tag === 'iframe' && attrName === 'srcdoc') {
|
|
679
|
-
ctx.
|
|
640
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.FORBIDDEN_IFRAME_SRCDOC_ATTRIBUTE, element);
|
|
680
641
|
}
|
|
681
642
|
}
|
|
682
643
|
}
|
|
683
644
|
function validateProperties(ctx, element) {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
645
|
+
const { props } = element;
|
|
646
|
+
if (props !== undefined) {
|
|
647
|
+
for (const propName in props) {
|
|
648
|
+
const propAttr = props[propName];
|
|
649
|
+
const { name: attrName, value } = propAttr;
|
|
650
|
+
if ((0, attribute_1.isProhibitedIsAttribute)(attrName)) {
|
|
651
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.IS_ATTRIBUTE_NOT_SUPPORTED, element);
|
|
652
|
+
}
|
|
653
|
+
if ((0, attribute_1.isTabIndexAttribute)(attrName) &&
|
|
654
|
+
!(0, ir_1.isIRExpressionAttribute)(propAttr) &&
|
|
655
|
+
!(0, attribute_1.isValidTabIndexAttributeValue)(value)) {
|
|
656
|
+
ctx.throwOnIRNode(errors_1.ParserDiagnostics.INVALID_TABINDEX_ATTRIBUTE, element);
|
|
657
|
+
}
|
|
695
658
|
}
|
|
696
659
|
}
|
|
697
660
|
}
|
|
698
|
-
function parseAttributes(ctx,
|
|
661
|
+
function parseAttributes(ctx, element, node) {
|
|
699
662
|
const parsedAttrs = new attribute_1.ParsedAttribute();
|
|
700
|
-
const { attrs: attributes
|
|
701
|
-
const { attrs: attrLocations } = parse5ElmLocation;
|
|
663
|
+
const { attrs: attributes } = node;
|
|
702
664
|
for (const attr of attributes) {
|
|
703
|
-
|
|
704
|
-
if (!attrLocation) {
|
|
705
|
-
throw new Error('An internal parsing error occurred while parsing attributes; attributes were found without a location.');
|
|
706
|
-
}
|
|
707
|
-
parsedAttrs.append(getTemplateAttribute(ctx, tagName, attr, attrLocation));
|
|
665
|
+
parsedAttrs.append(getTemplateAttribute(ctx, element, attr));
|
|
708
666
|
}
|
|
709
667
|
return parsedAttrs;
|
|
710
668
|
}
|
|
711
|
-
function getTemplateAttribute(ctx,
|
|
669
|
+
function getTemplateAttribute(ctx, element, attribute) {
|
|
670
|
+
const name = (0, attribute_1.attributeName)(attribute);
|
|
712
671
|
// Convert attribute name to lowercase because the location map keys follow the algorithm defined in the spec
|
|
713
672
|
// https://wicg.github.io/controls-list/html-output/multipage/syntax.html#attribute-name-state
|
|
714
|
-
const
|
|
715
|
-
const
|
|
673
|
+
const location = element.location.attrs[name.toLowerCase()];
|
|
674
|
+
const rawAttribute = ctx.getSource(location.startOffset, location.endOffset);
|
|
675
|
+
const { tag } = element;
|
|
716
676
|
// parse5 automatically converts the casing from camelcase to all lowercase. If the attribute name
|
|
717
677
|
// is not the same before and after the parsing, then the attribute name contains capital letters
|
|
718
|
-
|
|
719
|
-
if (!rawAttribute.startsWith(attrName)) {
|
|
678
|
+
if (!rawAttribute.startsWith(name)) {
|
|
720
679
|
ctx.throwAtLocation(errors_1.ParserDiagnostics.INVALID_ATTRIBUTE_CASE, location, [
|
|
721
680
|
rawAttribute,
|
|
722
681
|
tag,
|
|
@@ -724,28 +683,51 @@ function getTemplateAttribute(ctx, tag, attribute, attributeLocation) {
|
|
|
724
683
|
}
|
|
725
684
|
const isBooleanAttribute = !rawAttribute.includes('=');
|
|
726
685
|
const { value, escapedExpression } = (0, attribute_1.normalizeAttributeValue)(ctx, rawAttribute, tag, attribute, location);
|
|
727
|
-
let attrValue;
|
|
728
686
|
if ((0, expression_1.isExpression)(value) && !escapedExpression) {
|
|
729
|
-
|
|
687
|
+
return {
|
|
688
|
+
name,
|
|
689
|
+
location,
|
|
690
|
+
type: types_1.IRAttributeType.Expression,
|
|
691
|
+
value: (0, expression_1.parseExpression)(ctx, value, location),
|
|
692
|
+
};
|
|
730
693
|
}
|
|
731
694
|
else if (isBooleanAttribute) {
|
|
732
|
-
|
|
695
|
+
return {
|
|
696
|
+
name,
|
|
697
|
+
location,
|
|
698
|
+
type: types_1.IRAttributeType.Boolean,
|
|
699
|
+
value: true,
|
|
700
|
+
};
|
|
733
701
|
}
|
|
734
702
|
else {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
703
|
+
return {
|
|
704
|
+
name,
|
|
705
|
+
location,
|
|
706
|
+
type: types_1.IRAttributeType.String,
|
|
707
|
+
value,
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
function isInIteration(ctx, element) {
|
|
712
|
+
return ctx.findAncestor({
|
|
713
|
+
predicate: (element) => (0, ir_1.isTemplate)(element) && (element.forOf || element.forEach),
|
|
714
|
+
element,
|
|
715
|
+
});
|
|
741
716
|
}
|
|
742
|
-
function getForOfParent(ctx
|
|
743
|
-
return ctx.findAncestor(
|
|
717
|
+
function getForOfParent(ctx) {
|
|
718
|
+
return ctx.findAncestor({
|
|
719
|
+
predicate: (element) => element.forOf,
|
|
720
|
+
traversalCond: ({ current }) => (0, ir_1.isTemplate)(current),
|
|
721
|
+
});
|
|
744
722
|
}
|
|
745
|
-
function getForEachParent(ctx) {
|
|
746
|
-
return ctx.findAncestor(
|
|
723
|
+
function getForEachParent(ctx, element) {
|
|
724
|
+
return ctx.findAncestor({
|
|
725
|
+
element,
|
|
726
|
+
predicate: (element) => element.forEach,
|
|
727
|
+
traversalCond: ({ parent }) => parent && (0, ir_1.isTemplate)(parent),
|
|
728
|
+
});
|
|
747
729
|
}
|
|
748
|
-
function isInIteratorElement(ctx,
|
|
749
|
-
return !!(getForOfParent(ctx
|
|
730
|
+
function isInIteratorElement(ctx, element) {
|
|
731
|
+
return !!(getForOfParent(ctx) || getForEachParent(ctx, element));
|
|
750
732
|
}
|
|
751
733
|
//# sourceMappingURL=index.js.map
|