@marko/compiler 5.40.1 → 5.41.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/babel-utils.d.ts CHANGED
@@ -54,6 +54,10 @@ export interface TagDefinition {
54
54
  isRepeated?: boolean;
55
55
  targetProperty?: string;
56
56
  taglibId: string;
57
+ /** Set when the taglib was discovered by resolving an installed package. */
58
+ packageName?: string;
59
+ /** The root directory of `packageName`, which contains all files for this tag. */
60
+ packageRoot?: string;
57
61
  types?: string;
58
62
  template?: string;
59
63
  renderer?: string;
@@ -178,11 +182,14 @@ export function assertAllowedAttributes(
178
182
  path: t.NodePath<t.MarkoTag>,
179
183
  allowed: string[],
180
184
  ): void;
181
- export function assertNoArgs(path: t.NodePath<t.MarkoTag>): void;
185
+ export function assertNoArgs(path: t.NodePath<t.MarkoTag>, hint?: string): void;
182
186
  export function assertNoVar(path: t.NodePath<t.MarkoTag>): void;
183
187
  export function assertNoAttributes(path: t.NodePath<t.MarkoTag>): void;
184
188
  export function assertNoParams(path: t.NodePath<t.MarkoTag>): void;
185
- export function assertNoAttributeTags(path: t.NodePath<t.MarkoTag>): void;
189
+ export function assertNoAttributeTags(
190
+ path: t.NodePath<t.MarkoTag>,
191
+ hint?: string,
192
+ ): void;
186
193
  export function assertAttributesOrArgs(path: t.NodePath<t.MarkoTag>): void;
187
194
  export function assertAttributesOrSingleArg(path: t.NodePath<t.MarkoTag>): void;
188
195
 
@@ -300,7 +307,11 @@ export function parseTemplateLiteral(
300
307
  sourceEnd?: null | number,
301
308
  ): t.TemplateLiteral;
302
309
 
303
- export function resolveRelativePath(file: t.BabelFile, request: string): string;
310
+ export function resolveRelativePath(
311
+ file: t.BabelFile,
312
+ request: string,
313
+ tagDef?: TagDefinition,
314
+ ): string;
304
315
  export function importDefault(
305
316
  file: t.BabelFile,
306
317
  request: string,
@@ -323,6 +323,29 @@ function getMarkoFile(code, fileOpts, markoOpts) {
323
323
  try {
324
324
  file.___compileStage = "analyze";
325
325
  traverseAll(file, translator.analyze);
326
+
327
+ if (!markoOpts.errorRecovery) {
328
+ // Analyze failures are recorded as error diagnostics so the whole
329
+ // template reports at once (and editors compiling with
330
+ // `errorRecovery` keep them as recoverable diagnostics); mirror
331
+ // the parse layer by throwing them together at the stage's end.
332
+ const seen = new Set();
333
+ const errors = [];
334
+ for (const diag of meta.diagnostics) {
335
+ if (diag.type !== _diagnostics.DiagnosticType.Error) continue;
336
+ const key = `${
337
+ diag.loc ? `${diag.loc.start.line}:${diag.loc.start.column}` : ""}:${
338
+ diag.label}`;
339
+ if (seen.has(key)) continue;
340
+ seen.add(key);
341
+ errors.push(
342
+ (0, _buildCodeFrame.buildCodeFrameError)(filename, file.code, diag.loc, diag.label)
343
+ );
344
+ if (errors.length === 8) break;
345
+ }
346
+
347
+ (0, _mergeErrors.default)(errors);
348
+ }
326
349
  } catch (e) {
327
350
  compileCache.delete(id);
328
351
  throw e;
@@ -16,6 +16,22 @@ var _buildCodeFrame = require("../util/build-code-frame");
16
16
  var _mergeErrors = _interopRequireDefault(require("../util/merge-errors"));function _interopRequireDefault(e) {return e && e.__esModule ? e : { default: e };}
17
17
 
18
18
  const noop = () => {};
19
+ const jsxStyleAttrValueReg = /^\{[\s\S]*\}$/;
20
+ // A brace wrapped attribute value that only parses once unwrapped is almost
21
+ // certainly a JSX/Svelte style value (eg `onClick={handler}`); say so instead
22
+ // of surfacing the bare expression parse error.
23
+ const withWrappedAttrValueHint = (file, part, rawValue, node) => {
24
+ const trimmed = rawValue.trim();
25
+ if (
26
+ node.type === "MarkoParseError" &&
27
+ jsxStyleAttrValueReg.test(trimmed) &&
28
+ (0, _babelUtils.parseExpression)(file, trimmed.slice(1, -1), part.value.start).type !==
29
+ "MarkoParseError")
30
+ {
31
+ node.label += `${node.label.endsWith(".") ? "" : "."} Attribute values in Marko are plain JavaScript expressions, not JSX; remove the wrapping \`{ }\`.`;
32
+ }
33
+ return node;
34
+ };
19
35
  const emptyRange = (part) => part.start === part.end;
20
36
  const isAttrTag = (tag) => tag.name.value?.[0] === "@";
21
37
  const isStatementTag = (tag) => tag.tagDef?.parseOptions?.statement;
@@ -412,10 +428,12 @@ function parseMarko(file) {
412
428
  onAttrValue(part) {
413
429
  currentAttr.end = part.end;
414
430
  currentAttr.bound = part.bound;
415
- currentAttr.value = (0, _babelUtils.parseExpression)(
431
+ const rawAttrValue = parser.read(part.value);
432
+ currentAttr.value = withWrappedAttrValueHint(
416
433
  file,
417
- parser.read(part.value),
418
- part.value.start
434
+ part,
435
+ rawAttrValue,
436
+ (0, _babelUtils.parseExpression)(file, rawAttrValue, part.value.start)
419
437
  );
420
438
  },
421
439
 
@@ -41,23 +41,23 @@ function assertNoParams(path) {
41
41
  }
42
42
  }
43
43
 
44
- function assertNoAttributeTags(path) {
44
+ function assertNoAttributeTags(path, hint) {
45
45
  if (path.node.attributeTags.length) {
46
46
  throw path.hub.buildError(
47
47
  path.node.attributeTags[0],
48
- "Tag not support nested attribute tags."
48
+ `Tag does not support nested attribute tags.${hint ? ` ${hint}` : ""}`
49
49
  );
50
50
  }
51
51
  }
52
52
 
53
- function assertNoArgs(path) {
53
+ function assertNoArgs(path, hint) {
54
54
  const args = path.node.arguments;
55
55
  if (args && args.length) {
56
56
  const start = args[0].loc.start;
57
57
  const end = args[args.length - 1].loc.end;
58
58
  throw path.hub.buildError(
59
59
  { loc: { start, end } },
60
- "Tag does not support arguments."
60
+ `Tag does not support arguments.${hint ? ` ${hint}` : ""}`
61
61
  );
62
62
  }
63
63
  }
@@ -5,10 +5,13 @@ var _relativeImportPath = require("relative-import-path");function _interopRequi
5
5
 
6
6
  const IMPORTS_KEY = Symbol();
7
7
  const FS_START = _path.default.sep === "/" ? _path.default.sep : /^(.*?:)/.exec(_modules.cwd)[1];
8
+ const REG_NODE_MODULES = /[\\/]node_modules[\\/]/;
8
9
 
9
- function resolveRelativePath(file, request) {
10
+ function resolveRelativePath(file, request, tagDef) {
10
11
  if (request.startsWith(FS_START)) {
11
- request = (0, _relativeImportPath.relativeImportPath)(file.opts.filename, request);
12
+ request =
13
+ packageImportPath(file.opts.filename, tagDef, request) ||
14
+ (0, _relativeImportPath.relativeImportPath)(file.opts.filename, request);
12
15
  }
13
16
 
14
17
  if (file.markoOpts.optimize) {
@@ -21,6 +24,34 @@ function resolveRelativePath(file, request) {
21
24
  return request;
22
25
  }
23
26
 
27
+ /**
28
+ * A tag installed into `node_modules` is imported through the name its package was
29
+ * resolved by, since its path is a realpath which may not be importable at all.
30
+ */
31
+ function packageImportPath(from, tagDef, request) {
32
+ if (!tagDef?.packageName || !REG_NODE_MODULES.test(request)) return;
33
+
34
+ const { packageRoot } = tagDef;
35
+ // Within the package we stay relative, both because it always resolves and
36
+ // because a self reference would only work if the package exports the tag.
37
+ if (!isWithin(packageRoot, request) || isWithin(packageRoot, from)) return;
38
+
39
+ const subPath = _path.default.relative(packageRoot, request);
40
+ return `${tagDef.packageName}/${subPath.split(_path.default.sep).join("/")}`;
41
+ }
42
+
43
+ function isWithin(dir, filename) {
44
+ const rel = _path.default.relative(dir, filename);
45
+ // `path.relative` walks out of the dir with `..`, or returns an absolute path
46
+ // when it cannot relate the two at all, eg across windows drives.
47
+ return (
48
+ !!rel &&
49
+ rel !== ".." &&
50
+ !rel.startsWith(`..${_path.default.sep}`) &&
51
+ !_path.default.isAbsolute(rel));
52
+
53
+ }
54
+
24
55
  function importDefault(file, request, nameHint) {
25
56
  const imports = getImports(file);
26
57
  request = resolveRelativePath(file, request);
@@ -347,7 +347,8 @@ function resolveTagImport(path, request) {
347
347
  const tagName = request.slice(1, -1);
348
348
  const tagDef = (0, _taglib.getTagDefForTagName)(file, tagName);
349
349
  const tagEntry = tagDef && (tagDef.renderer || tagDef.template);
350
- const relativePath = tagEntry && (0, _imports.resolveRelativePath)(file, tagEntry);
350
+ const relativePath =
351
+ tagEntry && (0, _imports.resolveRelativePath)(file, tagEntry, tagDef);
351
352
 
352
353
  if (!relativePath) {
353
354
  throw path.buildCodeFrameError(
@@ -124,7 +124,9 @@ function find(dirname, registeredTaglibs, tagDiscoveryDirs) {
124
124
  rootPkg.__dirname
125
125
  );
126
126
  if (taglibPath) {
127
- var taglib = taglibLoader.loadTaglibFromFile(taglibPath, true);
127
+ // Resolving realpaths the taglib, which for a virtual store (eg pnpm) bears
128
+ // no relation to how it is imported, so hold onto the name it resolved by.
129
+ var taglib = taglibLoader.loadTaglibFromFile(taglibPath, true, name);
128
130
  addTaglib(taglib);
129
131
  }
130
132
  }
@@ -26,11 +26,12 @@ function handleImport(taglib, importedTaglib) {
26
26
  }
27
27
 
28
28
  class Taglib {
29
- constructor(filePath, isFromPackageJson) {
29
+ constructor(filePath, isFromPackageJson, packageName) {
30
30
  ok(filePath, '"filePath" expected');
31
31
  this.filePath = this.path /* deprecated */ = this.id = filePath;
32
32
  this.isFromPackageJson = isFromPackageJson === true;
33
33
  this.dirname = path.dirname(this.filePath);
34
+ this.packageName = packageName;
34
35
  this.scriptLang = undefined;
35
36
  this.tags = {};
36
37
  this.migrators = [];
@@ -72,6 +73,23 @@ class Taglib {
72
73
  }
73
74
  this.tags[tag.name] = tag;
74
75
  tag.taglibId = this.id || this.path;
76
+ this.setTagPackage(tag);
77
+ }
78
+
79
+ setPackageName(packageName) {
80
+ this.packageName = packageName;
81
+ for (var name in this.tags) {
82
+ if (hasOwnProperty.call(this.tags, name)) {
83
+ this.setTagPackage(this.tags[name]);
84
+ }
85
+ }
86
+ }
87
+
88
+ setTagPackage(tag) {
89
+ if (this.packageName) {
90
+ tag.packageName = this.packageName;
91
+ tag.packageRoot = this.dirname;
92
+ }
75
93
  }
76
94
 
77
95
  addImport(path) {
@@ -8,8 +8,8 @@ function loadTaglibFromProps(taglib, taglibProps) {
8
8
  return loaders.loadTaglibFromProps(taglib, taglibProps);
9
9
  }
10
10
 
11
- function loadTaglibFromFile(filePath, isFromPackageJson) {
12
- return loaders.loadTaglibFromFile(filePath, isFromPackageJson);
11
+ function loadTaglibFromFile(filePath, isFromPackageJson, packageName) {
12
+ return loaders.loadTaglibFromFile(filePath, isFromPackageJson, packageName);
13
13
  }
14
14
 
15
15
  function loadTaglibFromDir(filePath, tagDiscoveryDir) {
@@ -4,7 +4,7 @@ var jsonFileReader = require("./json-file-reader");
4
4
  var loaders = require("./loaders");
5
5
  var types = require("./types");
6
6
 
7
- function loadFromFile(filePath, isFromPackageJson) {
7
+ function loadFromFile(filePath, isFromPackageJson, packageName) {
8
8
  ok(filePath, '"filePath" is required');
9
9
 
10
10
  var taglib = cache.get(filePath);
@@ -12,11 +12,15 @@ function loadFromFile(filePath, isFromPackageJson) {
12
12
  // Only load a taglib once by caching the loaded taglibs using the file
13
13
  // system file path as the key
14
14
  if (!taglib) {
15
- taglib = new types.Taglib(filePath, isFromPackageJson);
15
+ taglib = new types.Taglib(filePath, isFromPackageJson, packageName);
16
16
  cache.put(filePath, taglib);
17
17
 
18
18
  var taglibProps = jsonFileReader.readFileSync(filePath);
19
19
  loaders.loadTaglibFromProps(taglib, taglibProps);
20
+ } else if (packageName && !taglib.packageName) {
21
+ // The taglib may have first been loaded by walking up from a file within the
22
+ // package itself, in which case we did not yet know the name it is installed as.
23
+ taglib.setPackageName(packageName);
20
24
  }
21
25
 
22
26
  return taglib;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marko/compiler",
3
- "version": "5.40.1",
3
+ "version": "5.41.0",
4
4
  "description": "Marko template to JS compiler.",
5
5
  "keywords": [
6
6
  "babel",
@@ -77,13 +77,13 @@
77
77
  "lasso-package-root": "^1.0.1",
78
78
  "raptor-regexp": "^1.0.1",
79
79
  "raptor-util": "^3.2.0",
80
- "relative-import-path": "^1.0.0",
80
+ "relative-import-path": "^1.0.1",
81
81
  "resolve-from": "^5.0.0",
82
82
  "self-closing-tags": "^1.0.1",
83
83
  "source-map-support": "^0.5.21"
84
84
  },
85
85
  "devDependencies": {
86
- "marko": "^5.39.14"
86
+ "marko": "^5.39.18"
87
87
  },
88
88
  "engines": {
89
89
  "node": "18 || 20 || >=22"