@lwc/template-compiler 2.45.2 → 2.45.3

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