@markuplint/vue-parser 5.3.0 → 5.5.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/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [5.5.0](https://github.com/markuplint/markuplint/compare/v5.4.0...v5.5.0) (2026-11-10)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **vue-parser:** treat a style and a script of the template as code that the compiler removes ([44315f6](https://github.com/markuplint/markuplint/commit/44315f692b852ec85f282d2632cf71e09f830594))
11
+
12
+
13
+ # [5.4.0](https://github.com/markuplint/markuplint/compare/v5.3.0...v5.4.0) (2026-10-29)
14
+
15
+ **Note:** Version bump only for package @markuplint/vue-parser
16
+
17
+
18
+
19
+
20
+
6
21
  # [5.3.0](https://github.com/markuplint/markuplint/compare/v5.2.0...v5.3.0) (2026-10-21)
7
22
 
8
23
  ### Bug Fixes
package/README.md CHANGED
@@ -24,4 +24,6 @@ Add `parser` option to your [configuration](https://markuplint.dev/configuration
24
24
  }
25
25
  ```
26
26
 
27
+ Vue removes a `<style>` and a `<script>` from a template, so no rule checks one and, when you set `severity.parseError`, each is reported as the parse error `vue-ignored-side-effect-tag`. See [`parse-error`](https://markuplint.dev/docs/migration/v4-to-v5/rules/parse-error).
28
+
27
29
  `parserOptions` option work in progress. In the current, the setting is `{ "sourceType": "module" }` that hard coded.
package/lib/parser.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ASTNode, ASTComment } from './vue-parser/index.js';
2
- import type { MLASTParentNode, MLASTNodeTreeItem } from '@markuplint/ml-ast';
2
+ import type { MLASTParentNode, MLASTNodeTreeItem, MLASTParseError } from '@markuplint/ml-ast';
3
3
  import { ParserError, Parser } from '@markuplint/parser-utils';
4
4
  type State = {
5
5
  comments: readonly ASTComment[];
@@ -30,6 +30,7 @@ declare class VueParser extends Parser<ASTNode, State> {
30
30
  tokenize(): {
31
31
  readonly ast: ASTNode[];
32
32
  readonly isFragment: boolean;
33
+ readonly parseErrors?: readonly MLASTParseError[];
33
34
  };
34
35
  parseError(error: any): ParserError;
35
36
  nodeize(originNode: ASTNode, parentNode: MLASTParentNode | null, depth: number): readonly MLASTNodeTreeItem[];
@@ -68,9 +69,9 @@ declare class VueParser extends Parser<ASTNode, State> {
68
69
  * The user's `authoredElementName` replaces all of this, as it replaces any default of a parser.
69
70
  *
70
71
  * @see https://vuejs.org/guide/essentials/component-basics#using-a-component
71
- * @see https://vuejs.org/api/built-in-components.html
72
+ * @see https://vuejs.org/api/built-in-components.html#built-in-components
72
73
  * @see https://vuejs.org/api/built-in-special-elements.html#built-in-special-elements
73
- * @see https://github.com/vuejs/core/blob/main/packages/compiler-core/src/parser.ts (`isComponent`)
74
+ * @see https://github.com/vuejs/core/blob/main/packages/compiler-core/src/parser.ts#isComponent
74
75
  */
75
76
  detectElementType(nodeName: string): import("@markuplint/ml-ast").ElementType;
76
77
  /**
package/lib/parser.js CHANGED
@@ -5,7 +5,7 @@ import { vueParse } from './vue-parser/index.js';
5
5
  * The built-in components, as written or in kebab-case: `isBuiltInType(tag, name)` of Vue is
6
6
  * `tag === name || tag === hyphenate(name)`.
7
7
  *
8
- * @see https://vuejs.org/api/built-in-components.html
8
+ * @see https://vuejs.org/api/built-in-components.html#built-in-components
9
9
  */
10
10
  const BUILT_IN_COMPONENTS = [
11
11
  'Transition',
@@ -19,6 +19,55 @@ const BUILT_IN_COMPONENTS = [
19
19
  'Suspense',
20
20
  'suspense',
21
21
  ];
22
+ /**
23
+ * Whether the element is a `<style>` or a `<script>` that Vue's compiler removes from a client template.
24
+ *
25
+ * > Tags with side effect (<script> and <style>) are ignored in client component templates.
26
+ *
27
+ * `ignoreSideEffectTags` of `@vue/compiler-dom` removes the element whose `tag` is `script` or `style`
28
+ * and whose `tagType` is `ELEMENT`: a name with a capital letter (`<Style>`) is a component
29
+ * (`isComponent` of `@vue/compiler-core`). The parser of `vue-eslint-parser` lower-cases `name` and
30
+ * keeps `rawName`, so `rawName` is what tells them apart.
31
+ *
32
+ * @see https://github.com/vuejs/core/blob/main/packages/compiler-dom/src/transforms/ignoreSideEffectTags.ts#ignoreSideEffectTags
33
+ * @see https://github.com/vuejs/core/blob/main/packages/compiler-dom/src/errors.ts#DOMErrorMessages
34
+ * @param element - The element of the template
35
+ * @returns `true` if the compiler removes the element
36
+ */
37
+ function isIgnoredSideEffectTag(element) {
38
+ return element.rawName === 'style' || element.rawName === 'script';
39
+ }
40
+ /**
41
+ * Collects the non-fatal parse errors for the elements that the compiler removes, in the order of the source.
42
+ * It does not look inside one: the compiler removes it with its content.
43
+ *
44
+ * @param nodes - The nodes of a level of the template
45
+ * @param rawCode - The source that the ranges of `nodes` are positions in
46
+ * @returns The parse errors, whose positions cover the whole element
47
+ */
48
+ function collectIgnoredSideEffectTags(nodes, rawCode) {
49
+ const errors = [];
50
+ for (const node of nodes) {
51
+ if (node.type !== 'VElement') {
52
+ continue;
53
+ }
54
+ if (isIgnoredSideEffectTag(node)) {
55
+ errors.push({
56
+ code: 'vue-ignored-side-effect-tag',
57
+ startOffset: node.range[0],
58
+ startLine: node.loc.start.line,
59
+ startCol: node.loc.start.column + 1,
60
+ endOffset: node.range[1],
61
+ endLine: node.loc.end.line,
62
+ endCol: node.loc.end.column + 1,
63
+ raw: rawCode.slice(node.range[0], node.range[1]),
64
+ });
65
+ continue;
66
+ }
67
+ errors.push(...collectIgnoredSideEffectTags(node.children, rawCode));
68
+ }
69
+ return errors;
70
+ }
22
71
  /**
23
72
  * Directive resolution is intentionally not performed by this parser:
24
73
  * `directivePatterns` declared in `@markuplint/vue-spec` are applied later by
@@ -60,9 +109,12 @@ class VueParser extends Parser {
60
109
  if (ast.templateBody?.comments) {
61
110
  this.state.comments = ast.templateBody.comments;
62
111
  }
112
+ const children = ast.templateBody?.children ?? [];
113
+ const parseErrors = collectIgnoredSideEffectTags(children, this.rawCode);
63
114
  return {
64
- ast: ast.templateBody?.children ?? [],
115
+ ast: children,
65
116
  isFragment: true,
117
+ ...(parseErrors.length > 0 ? { parseErrors } : {}),
66
118
  };
67
119
  }
68
120
  parseError(error) {
@@ -81,6 +133,17 @@ class VueParser extends Parser {
81
133
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
82
134
  originNode, parentNode, depth) {
83
135
  const token = this.sliceFragment(originNode.range[0], originNode.range[1]);
136
+ // The compiler removes it, so neither its content nor where it is written reaches the DOM:
137
+ // it is a block of code that no rule reads, and `tokenize` reports it as a parse error.
138
+ if (originNode.type === 'VElement' && isIgnoredSideEffectTag(originNode)) {
139
+ return this.visitPsBlock({
140
+ ...token,
141
+ depth,
142
+ parentNode,
143
+ nodeName: originNode.rawName,
144
+ isFragment: false,
145
+ });
146
+ }
84
147
  switch (originNode.type) {
85
148
  case 'VText': {
86
149
  return this.visitText({
@@ -199,9 +262,9 @@ class VueParser extends Parser {
199
262
  * The user's `authoredElementName` replaces all of this, as it replaces any default of a parser.
200
263
  *
201
264
  * @see https://vuejs.org/guide/essentials/component-basics#using-a-component
202
- * @see https://vuejs.org/api/built-in-components.html
265
+ * @see https://vuejs.org/api/built-in-components.html#built-in-components
203
266
  * @see https://vuejs.org/api/built-in-special-elements.html#built-in-special-elements
204
- * @see https://github.com/vuejs/core/blob/main/packages/compiler-core/src/parser.ts (`isComponent`)
267
+ * @see https://github.com/vuejs/core/blob/main/packages/compiler-core/src/parser.ts#isComponent
205
268
  */
206
269
  detectElementType(nodeName) {
207
270
  return super.detectElementType(nodeName, [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/vue-parser",
3
- "version": "5.3.0",
3
+ "version": "5.5.0",
4
4
  "description": "Vue parser for markuplint",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,11 +32,11 @@
32
32
  "clean": "tsc --build --clean tsconfig.build.json"
33
33
  },
34
34
  "dependencies": {
35
- "@markuplint/html-parser": "5.3.0",
36
- "@markuplint/ml-ast": "5.3.0",
37
- "@markuplint/parser-utils": "5.3.0",
35
+ "@markuplint/html-parser": "5.5.0",
36
+ "@markuplint/ml-ast": "5.5.0",
37
+ "@markuplint/parser-utils": "5.5.0",
38
38
  "@vue/shared": "^3.5.44",
39
39
  "vue-eslint-parser": "^10.4.1"
40
40
  },
41
- "gitHead": "f587cf0206c8426e57369a48d6d5fb22306eafde"
41
+ "gitHead": "fedebcfd56eaec4642e68643b2f9e10750d810e1"
42
42
  }