@marko/compiler 5.37.20 → 5.37.22

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.
@@ -49,7 +49,28 @@ function parseMarko(file) {
49
49
  return node;
50
50
  };
51
51
  const enterTag = (node) => {
52
- currentTag = currentBody.pushContainer("body", node)[0];
52
+ if (isAttrTag(node)) {
53
+ if (currentTag === file.path) {
54
+ throw file.buildCodeFrameError(
55
+ node.name,
56
+ "@tags must be nested within another element."
57
+ );
58
+ }
59
+
60
+ let previousSiblingIndex = currentBody.length;
61
+ while (previousSiblingIndex) {
62
+ let previousSibling = currentBody[--previousSiblingIndex];
63
+ if (!t.isMarkoComment(previousSibling)) {
64
+ break;
65
+ }
66
+ currentTag.pushContainer("attributeTags", previousSibling.node);
67
+ currentBody.get("body").get(previousSiblingIndex).remove();
68
+ }
69
+
70
+ currentTag = currentTag.pushContainer("attributeTags", node)[0];
71
+ } else {
72
+ currentTag = currentBody.pushContainer("body", node)[0];
73
+ }
53
74
  currentBody = currentTag.get("body");
54
75
  onNext(node);
55
76
  };
@@ -496,7 +517,8 @@ function parseMarko(file) {
496
517
  },
497
518
  onCloseTagEnd(part) {
498
519
  const { node } = currentTag;
499
- const parserPlugin = node.tagDef?.parser;
520
+ const tagDef = node.tagDef;
521
+ const parserPlugin = tagDef?.parser;
500
522
  if (preservingWhitespaceUntil === node) {
501
523
  preservingWhitespaceUntil = undefined;
502
524
  }
@@ -510,9 +532,54 @@ function parseMarko(file) {
510
532
  (hook.default || hook)(currentTag, t);
511
533
  }
512
534
 
513
- currentTag = currentTag.parentPath.parentPath;
535
+ const parentTag = isAttrTag(node) ?
536
+ currentTag.parentPath :
537
+ currentTag.parentPath.parentPath;
538
+ const { attributeTags } = node;
539
+
540
+ if (attributeTags.length) {
541
+ const isControlFlow = tagDef?.parseOptions?.controlFlow;
542
+
543
+ if (node.body.body.length) {
544
+ const body = [];
545
+ for (const child of node.body.body) {
546
+ if (
547
+ t.isMarkoScriptlet(child) ||
548
+ isControlFlow && (
549
+ t.isMarkoComment(child) ||
550
+ child.tagDef?.controlFlow && !child.body.body.length))
551
+ {
552
+ // When we have a control flow with mixed body and attribute tag content
553
+ // we move any scriptlets, comments or empty nested control flow.
554
+ // This is because they initially ambiguous as to whether
555
+ // they are part of the body or the attributeTags.
556
+ attributeTags.push(child);
557
+ } else {
558
+ body.push(child);
559
+ }
560
+ }
561
+
562
+ if (isControlFlow && body.length) {
563
+ onNext();
564
+ throw file.buildCodeFrameError(
565
+ body[0],
566
+ "Cannot have attribute tags and body content under a control flow tag."
567
+ );
568
+ } else {
569
+ node.body.body = body;
570
+ }
514
571
 
515
- if (currentTag) {
572
+ attributeTags.sort(sortByStart);
573
+ }
574
+
575
+ if (isControlFlow) {
576
+ currentTag.remove();
577
+ parentTag.pushContainer("attributeTags", node);
578
+ }
579
+ }
580
+
581
+ if (parentTag) {
582
+ currentTag = parentTag;
516
583
  currentBody = currentTag.get("body");
517
584
  } else {
518
585
  currentTag = currentBody = file.path;
@@ -533,4 +600,8 @@ function parseMarko(file) {
533
600
  start: { line: 1, column: 0 },
534
601
  end: positionAt(ast.end)
535
602
  };
603
+ }
604
+
605
+ function sortByStart(a, b) {
606
+ return a.start - b.start;
536
607
  }
@@ -30,7 +30,7 @@ Object.assign(_printer.default.prototype, {
30
30
  },
31
31
  MarkoPlaceholder(node, parent) {
32
32
  if (parent) {
33
- const parentBody = parent.body;
33
+ const parentBody = getBody(parent);
34
34
  const prev = parentBody[parentBody.indexOf(node) - 1];
35
35
 
36
36
  if (prev && (t.isMarkoText(prev) || t.isMarkoPlaceholder(prev))) {
@@ -45,7 +45,7 @@ Object.assign(_printer.default.prototype, {
45
45
  MarkoScriptlet(node, parent) {
46
46
  this.removeTrailingNewline();
47
47
 
48
- if (!(t.isProgram(parent) && parent.body.indexOf(node) === 0)) {
48
+ if (!(t.isProgram(parent) && getBody(parent)[0] === node)) {
49
49
  this.token("\n");
50
50
  }
51
51
 
@@ -109,7 +109,7 @@ Object.assign(_printer.default.prototype, {
109
109
  printWithParansIfNeeded.call(this, node.value);
110
110
  },
111
111
  MarkoText(node, parent) {
112
- const parentBody = parent.body;
112
+ const parentBody = getBody(parent);
113
113
  const prev = parentBody[parentBody.indexOf(node) - 1];
114
114
  const concatToPrev = prev && t.isMarkoPlaceholder(prev);
115
115
  let { value } = node;
@@ -213,14 +213,14 @@ Object.assign(_printer.default.prototype, {
213
213
  if (_selfClosingTags.default.voidElements.includes(tagName)) {
214
214
  this.token(">");
215
215
  } else if (
216
- !node.body.body.length ||
216
+ !(node.body.body.length || node.attributeTags.length) ||
217
217
  _selfClosingTags.default.svgElements.includes(tagName))
218
218
  {
219
219
  this.token("/>");
220
220
  } else {
221
221
  this.token(">");
222
222
  this.newline();
223
- this.print(node.body);
223
+ this.printSequence(zipAttributeTagsAndBody(node), { indent: true });
224
224
  this.token("</");
225
225
  if (!isDynamicTag) {
226
226
  this.token(tagName);
@@ -269,4 +269,66 @@ function statementCouldHaveUnenclosedNewline(node) {
269
269
  default:
270
270
  return false;
271
271
  }
272
+ }
273
+
274
+ function zipAttributeTagsAndBody(tag) {
275
+ const {
276
+ attributeTags,
277
+ body: { body }
278
+ } = tag;
279
+ const bodyLen = body.length;
280
+ const attributeTagsLen = attributeTags.length;
281
+ if (!attributeTagsLen) return body;
282
+ if (!bodyLen) return attributeTags;
283
+
284
+ const result = [];
285
+ let i = 0;
286
+ let j = 0;
287
+
288
+ while (i < bodyLen && j < attributeTagsLen) {
289
+ const bodyNode = body[i];
290
+ const attributeTag = attributeTags[j];
291
+
292
+ if (bodyNode.loc != null && attributeTag.loc != null) {
293
+ if (compareStartLoc(bodyNode, attributeTag) < 0) {
294
+ result.push(bodyNode);
295
+ i++;
296
+ } else {
297
+ result.push(attributeTag);
298
+ j++;
299
+ }
300
+ } else if (j < attributeTagsLen) {
301
+ result.push(attributeTag);
302
+ j++;
303
+ } else {
304
+ result.push(bodyNode);
305
+ i++;
306
+ }
307
+ }
308
+
309
+ while (j < attributeTagsLen) {
310
+ result.push(attributeTags[j++]);
311
+ }
312
+
313
+ while (i < bodyLen) {
314
+ result.push(body[i++]);
315
+ }
316
+
317
+ return result;
318
+ }
319
+
320
+ function getBody(parent) {
321
+ switch (parent.type) {
322
+ case "MarkoTag":
323
+ return parent.body.body;
324
+ default:
325
+ return parent.body;
326
+ }
327
+ }
328
+
329
+ function compareStartLoc(a, b) {
330
+ return (
331
+ a.loc.start.line - b.loc.start.line ||
332
+ a.loc.start.column - b.loc.start.column);
333
+
272
334
  }
@@ -180,14 +180,22 @@ const MarkoDefinitions = {
180
180
 
181
181
  MarkoTag: {
182
182
  aliases: ["Marko", "Statement"],
183
- builder: ["name", "attributes", "body", "arguments", "var"],
184
- visitor: [
183
+ builder: [
185
184
  "name",
186
- "typeArguments",
187
185
  "attributes",
188
186
  "body",
189
187
  "arguments",
190
- "var"],
188
+ "var",
189
+ "attributeTags"],
190
+
191
+ visitor: [
192
+ "name",
193
+ "typeArguments",
194
+ "arguments",
195
+ "attributes",
196
+ "attributeTags",
197
+ "var",
198
+ "body"],
191
199
 
192
200
  fields: {
193
201
  name: {
@@ -218,6 +226,10 @@ const MarkoDefinitions = {
218
226
  var: {
219
227
  validate: (0, _utils.assertNodeType)("LVal"),
220
228
  optional: true
229
+ },
230
+ attributeTags: {
231
+ validate: arrayOfType(["MarkoTag", "MarkoScriptlet", "MarkoComment"]),
232
+ default: []
221
233
  }
222
234
  }
223
235
  }
@@ -37,26 +37,16 @@ function buildLookup(dirname, requestedTranslator, onError) {
37
37
  loadedTranslatorsTaglibs.set(
38
38
  translator,
39
39
  taglibsForDir = registeredTaglibs.concat(
40
- resolveOptionalTaglibs(translator.optionalTaglibs || []).
40
+ resolveOptionalTaglibs(translator.optionalTaglibs || [], onError).
41
41
  concat(translator.taglibs).
42
42
  map(([id, props]) => loadTaglib(id, props))
43
43
  )
44
44
  );
45
45
  }
46
46
 
47
- if (onError) {
48
- const prevOnError = _config.default.onError;
49
- _config.default.onError = onError;
50
- try {
51
- taglibsForDir = _finder2.default.find(dirname, taglibsForDir);
52
- } catch (err) {
53
- _config.default.onError(err);
54
- } finally {
55
- _config.default.onError = prevOnError;
56
- }
57
- } else {
47
+ runAndCatchErrors(() => {
58
48
  taglibsForDir = _finder2.default.find(dirname, taglibsForDir);
59
- }
49
+ }, onError);
60
50
 
61
51
  const cacheKey = taglibsForDir.
62
52
  map((it) => it.id).
@@ -95,11 +85,13 @@ function clearCaches() {
95
85
  lookupCache = Object.create(null);
96
86
  }
97
87
 
98
- function resolveOptionalTaglibs(taglibIds) {
88
+ function resolveOptionalTaglibs(taglibIds, onError) {
99
89
  const resolvedTaglibs = [];
100
90
  for (const id of taglibIds) {
101
91
  if (hasRootDependency(id)) {
102
- resolvedTaglibs.push(resolveTaglib(id));
92
+ runAndCatchErrors(() => {
93
+ resolvedTaglibs.push(resolveTaglib(id));
94
+ }, onError);
103
95
  }
104
96
  }
105
97
 
@@ -110,6 +102,22 @@ function resolveOptionalTaglibs(taglibIds) {
110
102
  const _loader = exports._loader = _loader2.default;
111
103
  const _finder = exports._finder = _finder2.default;
112
104
 
105
+ function runAndCatchErrors(fn, onError) {
106
+ if (onError) {
107
+ const prevOnError = _config.default.onError;
108
+ _config.default.onError = onError;
109
+ try {
110
+ fn();
111
+ } catch (err) {
112
+ _config.default.onError(err);
113
+ } finally {
114
+ _config.default.onError = prevOnError;
115
+ }
116
+ } else {
117
+ fn();
118
+ }
119
+ }
120
+
113
121
  function loadTaglib(id, props) {
114
122
  return _loader2.default.loadTaglibFromProps(_loader2.default.createTaglib(id), props);
115
123
  }
package/dist/types.d.ts CHANGED
@@ -1751,6 +1751,7 @@ export interface MarkoTag extends BaseNode {
1751
1751
  body: MarkoTagBody;
1752
1752
  arguments: Array<Expression | SpreadElement> | null;
1753
1753
  var: LVal | null;
1754
+ attributeTags: Array<MarkoTag | MarkoScriptlet | MarkoComment>;
1754
1755
  rawValue: string | null;
1755
1756
  typeArguments: TSTypeParameterInstantiation | null;
1756
1757
  }
@@ -2147,7 +2148,7 @@ export function markoClass(body: ClassBody): MarkoClass;
2147
2148
  export function markoAttribute(name: string, value: Expression, modifier?: string | null, _arguments?: Array<Expression | SpreadElement> | null, _default?: boolean | null, bound?: boolean | null): MarkoAttribute;
2148
2149
  export function markoSpreadAttribute(value: Expression): MarkoSpreadAttribute;
2149
2150
  export function markoTagBody(body?: Array<MarkoTag | MarkoCDATA | MarkoText | MarkoPlaceholder | MarkoScriptlet | MarkoComment>, params?: Array<Identifier | Pattern | RestElement>): MarkoTagBody;
2150
- export function markoTag(name: Expression, attributes: Array<MarkoAttribute | MarkoSpreadAttribute> | undefined, body: MarkoTagBody, _arguments?: Array<Expression | SpreadElement> | null, _var?: LVal | null): MarkoTag;
2151
+ export function markoTag(name: Expression, attributes: Array<MarkoAttribute | MarkoSpreadAttribute> | undefined, body: MarkoTagBody, _arguments?: Array<Expression | SpreadElement> | null, _var?: LVal | null, attributeTags?: Array<MarkoTag | MarkoScriptlet | MarkoComment>): MarkoTag;
2151
2152
  export function isAccessor(node: object | null | undefined, opts?: object | null): node is Accessor;
2152
2153
  export function assertAccessor(node: object | null | undefined, opts?: object | null): void;
2153
2154
  export function isAnyTypeAnnotation(node: object | null | undefined, opts?: object | null): node is AnyTypeAnnotation;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marko/compiler",
3
- "version": "5.37.20",
3
+ "version": "5.37.22",
4
4
  "description": "Marko template to JS compiler.",
5
5
  "keywords": [
6
6
  "babel",
@@ -58,7 +58,7 @@
58
58
  "@babel/code-frame": "^7.26.0",
59
59
  "@babel/core": "^7.26.0",
60
60
  "@babel/generator": "^7.26.0",
61
- "@babel/parser": "^7.26.0",
61
+ "@babel/parser": "^7.26.1",
62
62
  "@babel/plugin-syntax-typescript": "^7.25.9",
63
63
  "@babel/plugin-transform-modules-commonjs": "^7.25.9",
64
64
  "@babel/plugin-transform-typescript": "^7.25.9",
@@ -66,7 +66,7 @@
66
66
  "@babel/traverse": "^7.25.9",
67
67
  "@babel/types": "^7.26.0",
68
68
  "@luxass/strip-json-comments": "^1.3.2",
69
- "@marko/babel-utils": "^6.5.8",
69
+ "@marko/babel-utils": "^6.5.9",
70
70
  "complain": "^1.6.1",
71
71
  "he": "^1.2.0",
72
72
  "htmljs-parser": "^5.5.2",
@@ -80,7 +80,7 @@
80
80
  "source-map-support": "^0.5.21"
81
81
  },
82
82
  "devDependencies": {
83
- "@marko/translator-default": "^6.0.21"
83
+ "@marko/translator-default": "^6.0.22"
84
84
  },
85
85
  "publishConfig": {
86
86
  "access": "public"