UNPKG

3.96 kBJavaScriptView Raw
1const {join, dirname, relative, basename} = require('path');
2const resolve = require('eslint-module-utils/resolve').default;
3const pkgDir = require('pkg-dir');
4
5function 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
18function 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
32const DEFAULT_IMPORT = Symbol('default');
33const NAMESPACE_IMPORT = Symbol('namespace');
34
35function 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
66const INDEX_FILE = /^index\./;
67const IS_FILE = /(^|\/).*\..*$/;
68function 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
93function 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
109function 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
132function getRootObject(memberExpression) {
133 let currentObject = memberExpression;
134
135 while (currentObject.object) {
136 currentObject = currentObject.object;
137 }
138
139 return currentObject;
140}
141
142module.exports = {
143 uncast,
144 getName,
145 getImportDetailsForName,
146 polarisComponentFromJSX,
147 getRootObject,
148 DEFAULT_IMPORT,
149 NAMESPACE_IMPORT,
150};