• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

future-architect / eslint-plugin-vue-scoped-css / 14774400750

pending completion
14774400750

Pull #392

github

web-flow
Merge bc68239aa into 8e9b9c3d4
Pull Request #392: chore(deps): update dependency eslint-plugin-node-dependencies to v1

1454 of 1669 branches covered (87.12%)

Branch coverage included in aggregate %.

2778 of 2917 relevant lines covered (95.23%)

406.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.93
/lib/styles/context/vue-components/find-vue.ts
1
import { AST } from "vue-eslint-parser";
1✔
2
import type { ASTNode, RuleContext } from "../../../types";
3
import { unwrapTypesExpression } from "../../utils/nodes";
1✔
4
import { getSourceCode } from "../../../utils/compat";
1✔
5

6
const traverseNodes = AST.traverseNodes;
1✔
7

8
/**
9
 * If the given node is a Vue component, returns an Object that defines the Vue component.
10
 * @param node Node to check
11
 */
12
function getVueComponentObject(
13
  node: ASTNode,
14
): AST.ESLintObjectExpression | null {
15
  if (node.type !== "ExportDefaultDeclaration") {
53!
16
    return null;
×
17
  }
18
  const declaration = unwrapTypesExpression(node.declaration);
53✔
19
  if (declaration.type === "ObjectExpression") {
53✔
20
    return declaration;
49✔
21
  }
22
  if (
4✔
23
    declaration.type === "CallExpression" &&
8✔
24
    declaration.arguments.length >= 1
25
  ) {
26
    const callee = declaration.callee;
4✔
27

28
    if (callee.type === "MemberExpression") {
4✔
29
      const calleeObject = unwrapTypesExpression(callee.object);
2✔
30

31
      if (
2✔
32
        calleeObject.type === "Identifier" &&
8✔
33
        calleeObject.name === "Vue" &&
34
        callee.property.type === "Identifier" &&
35
        callee.property.name === "extend"
36
      ) {
37
        // for Vue.js 2.x
38
        // Vue.extend({})
39
        const object = unwrapTypesExpression(declaration.arguments[0]);
2✔
40
        if (object.type === "ObjectExpression") {
2✔
41
          return object;
2✔
42
        }
43
      }
44
    }
45
    if (callee.type === "Identifier") {
2✔
46
      if (callee.name === "defineComponent") {
2✔
47
        // for Vue.js 3.x
48
        // defineComponent({})
49
        const object = unwrapTypesExpression(declaration.arguments[0]);
2✔
50
        if (object.type === "ObjectExpression") {
2✔
51
          return object;
2✔
52
        }
53
      }
54
    }
55
  }
56
  return null;
×
57
}
58

59
const vueComponentCache = new WeakMap<
1✔
60
  RuleContext,
61
  { component: AST.ESLintObjectExpression | null; cachedAt: number }
62
>();
63

64
/**
65
 * Find Vue component of the current file.
66
 * @param {RuleContext} context The ESLint rule context object.
67
 * @returns {ASTNode|null} Vue component
68
 */
69
function findVueComponent(
70
  context: RuleContext,
71
): AST.ESLintObjectExpression | null {
72
  const cached = vueComponentCache.get(context);
1,162✔
73
  if (cached !== undefined && cached.cachedAt > Date.now() - 1000) {
1,162✔
74
    return cached.component;
997✔
75
  }
76

77
  const sourceCode = getSourceCode(context);
165✔
78
  const componentComments = sourceCode
165✔
79
    .getAllComments()
80
    .filter((comment) => comment.value.includes("@vue/component"));
4✔
81
  const foundNodes: ASTNode[] = [];
165✔
82

83
  /**
84
   * Checks whether the given node is duplicate.
85
   * @param {object} node the node to check
86
   * @returns {boolean} `true` if the given node is duplicate.
87
   */
88
  function isDuplicateNode(node: ASTNode) {
89
    if (foundNodes.some((el) => el.loc.start.line === node.loc.start.line)) {
53!
90
      return true;
×
91
    }
92
    foundNodes.push(node);
53✔
93
    return false;
53✔
94
  }
95

96
  let result:
97
    | AST.ESLintObjectExpression
98
    | AST.ESLintDeclaration
99
    | AST.ESLintSpreadElement
100
    | null = null;
165✔
101
  let breakNode = false;
165✔
102
  traverseNodes(sourceCode.ast, {
165✔
103
    visitorKeys: sourceCode.visitorKeys,
104
    enterNode(node) {
105
      if (breakNode) {
1,358✔
106
        return;
1,126✔
107
      }
108
      if (node.type === "ObjectExpression") {
232!
109
        if (
×
110
          !componentComments.some(
×
111
            (el) => el.loc.end.line === node.loc.start.line - 1,
×
112
          ) ||
113
          isDuplicateNode(node)
114
        ) {
115
          return;
×
116
        }
117
        result = node;
×
118
      } else if (node.type === "ExportDefaultDeclaration") {
232✔
119
        // export default {} in .vue
120
        const vueNode = getVueComponentObject(node);
53✔
121
        if (!vueNode || isDuplicateNode(vueNode)) {
53!
122
          return;
×
123
        }
124
        result = vueNode;
53✔
125
        breakNode = Boolean(node);
53✔
126
      }
127
    },
128
    leaveNode() {
129
      // noop
130
    },
131
  });
132

133
  vueComponentCache.set(context, {
165✔
134
    component: result,
135
    cachedAt: Date.now(),
136
  });
137
  return result;
165✔
138
}
139

140
export default findVueComponent;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc