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