1 | const {join, dirname, relative, basename} = require('path');
|
2 | const resolve = require('eslint-module-utils/resolve').default;
|
3 | const pkgDir = require('pkg-dir');
|
4 |
|
5 | function uncast(node) {
|
6 | let currentNode = node;
|
7 |
|
8 | while (
|
9 | currentNode.type === 'TypeCastExpression' ||
|
10 | currentNode.type === 'TSAsExpression'
|
11 | ) {
|
12 | currentNode = currentNode.expression;
|
13 | }
|
14 |
|
15 | return currentNode;
|
16 | }
|
17 |
|
18 | function getName(node) {
|
19 | const finalNode = uncast(node);
|
20 | const type = finalNode.type;
|
21 |
|
22 | if (type === 'Identifier') {
|
23 | return finalNode.name;
|
24 | } else if (type === 'Literal') {
|
25 | return String(finalNode.value);
|
26 | } else if (type === 'TemplateLiteral' && finalNode.expressions.length === 0) {
|
27 | return finalNode.quasis[0].value.raw;
|
28 | }
|
29 | return null;
|
30 | }
|
31 |
|
32 | const DEFAULT_IMPORT = Symbol('default');
|
33 | const NAMESPACE_IMPORT = Symbol('namespace');
|
34 |
|
35 | function getImportDetailsForName(name, context) {
|
36 | const definition = findDefinition(name, context);
|
37 | if (definition == null || definition.type !== 'ImportBinding') {
|
38 | return null;
|
39 | }
|
40 |
|
41 | const source = definition.parent.source.value;
|
42 | const resolvedSource = resolve(source, context);
|
43 | if (resolvedSource == null) {
|
44 | return null;
|
45 | }
|
46 |
|
47 | const definitionNode = definition.node;
|
48 | let imported;
|
49 |
|
50 | if (definitionNode.type === 'ImportDefaultSpecifier') {
|
51 | imported = DEFAULT_IMPORT;
|
52 | } else if (definitionNode.type === 'ImportNamespaceSpecifier') {
|
53 | imported = NAMESPACE_IMPORT;
|
54 | } else {
|
55 | const importedName = definitionNode.imported.name;
|
56 | imported = importedName === 'default' ? DEFAULT_IMPORT : importedName;
|
57 | }
|
58 |
|
59 | return {
|
60 | source: normalizeSource(resolvedSource, context),
|
61 | local: name,
|
62 | imported,
|
63 | };
|
64 | }
|
65 |
|
66 | const INDEX_FILE = /^index\./;
|
67 | const IS_FILE = /(^|\/).*\..*$/;
|
68 | function normalizeSource(source, context) {
|
69 | const sourceRoot = pkgDir.sync(source);
|
70 | const packageRoot = pkgDir.sync(context.getFilename());
|
71 | const relativeSource =
|
72 | sourceRoot === packageRoot
|
73 | ? relative(packageRoot, source)
|
74 | : relative(packageRoot, sourceRoot);
|
75 | const sourceBasename = basename(relativeSource);
|
76 | const sourceDir = IS_FILE.test(relativeSource)
|
77 | ? dirname(relativeSource)
|
78 | : relativeSource;
|
79 |
|
80 | const sourceWithoutExtension = INDEX_FILE.test(sourceBasename)
|
81 | ? sourceDir
|
82 | : join(
|
83 | sourceDir,
|
84 | sourceBasename
|
85 | .split('.')
|
86 | .slice(0, -1)
|
87 | .join('.'),
|
88 | );
|
89 |
|
90 | return sourceWithoutExtension.replace(/^node_modules\//, '');
|
91 | }
|
92 |
|
93 | function findDefinition(name, context) {
|
94 | let definition = null;
|
95 | let currentScope = context.getScope();
|
96 |
|
97 | while (currentScope && !definition) {
|
98 | if (currentScope.set.has(name)) {
|
99 | const {defs} = currentScope.set.get(name);
|
100 | definition = defs[defs.length - 1];
|
101 | }
|
102 |
|
103 | currentScope = currentScope.upper;
|
104 | }
|
105 |
|
106 | return definition;
|
107 | }
|
108 |
|
109 | function polarisComponentFromJSX({openingElement}, context) {
|
110 | const isMemberExpression = openingElement.name.type === 'JSXMemberExpression';
|
111 | const importDetails = isMemberExpression
|
112 | ? getImportDetailsForName(getRootObject(openingElement.name).name, context)
|
113 | : getImportDetailsForName(openingElement.name.name, context);
|
114 |
|
115 | if (importDetails == null || importDetails.source !== '@shopify/polaris') {
|
116 | return false;
|
117 | }
|
118 |
|
119 | const sourceCode = context.getSourceCode();
|
120 | const name = sourceCode.getText(openingElement.name);
|
121 |
|
122 | return isMemberExpression &&
|
123 | (importDetails.imported === NAMESPACE_IMPORT ||
|
124 | importDetails.imported === DEFAULT_IMPORT)
|
125 | ? name.replace(
|
126 | `${sourceCode.getText(getRootObject(openingElement.name))}.`,
|
127 | '',
|
128 | )
|
129 | : name;
|
130 | }
|
131 |
|
132 | function getRootObject(memberExpression) {
|
133 | let currentObject = memberExpression;
|
134 |
|
135 | while (currentObject.object) {
|
136 | currentObject = currentObject.object;
|
137 | }
|
138 |
|
139 | return currentObject;
|
140 | }
|
141 |
|
142 | module.exports = {
|
143 | uncast,
|
144 | getName,
|
145 | getImportDetailsForName,
|
146 | polarisComponentFromJSX,
|
147 | getRootObject,
|
148 | DEFAULT_IMPORT,
|
149 | NAMESPACE_IMPORT,
|
150 | };
|