1 | 'use strict';
|
2 | const getDocumentationUrl = require('./utils/get-documentation-url');
|
3 | const quoteString = require('./utils/quote-string');
|
4 | const replaceTemplateElement = require('./utils/replace-template-element');
|
5 | const escapeTemplateElementRaw = require('./utils/escape-template-element-raw');
|
6 |
|
7 | const ignoredIdentifier = new Set([
|
8 | 'gql',
|
9 | 'html',
|
10 | 'svg'
|
11 | ]);
|
12 |
|
13 | const ignoredMemberExpressionObject = new Set([
|
14 | 'styled'
|
15 | ]);
|
16 |
|
17 | const isIgnoredTag = node => {
|
18 | if (!node.parent || !node.parent.parent || !node.parent.parent.tag) {
|
19 | return false;
|
20 | }
|
21 |
|
22 | const {tag} = node.parent.parent;
|
23 |
|
24 | if (tag.type === 'Identifier' && ignoredIdentifier.has(tag.name)) {
|
25 | return true;
|
26 | }
|
27 |
|
28 | if (tag.type === 'MemberExpression') {
|
29 | const {object} = tag;
|
30 | if (
|
31 | object.type === 'Identifier' &&
|
32 | ignoredMemberExpressionObject.has(object.name)
|
33 | ) {
|
34 | return true;
|
35 | }
|
36 | }
|
37 |
|
38 | return false;
|
39 | };
|
40 |
|
41 | const defaultMessage = 'Prefer `{{suggest}}` over `{{match}}`.';
|
42 | const SUGGESTION_MESSAGE_ID = 'replace';
|
43 |
|
44 | function getReplacements(patterns) {
|
45 | return Object.entries(patterns)
|
46 | .map(([match, options]) => {
|
47 | if (typeof options === 'string') {
|
48 | options = {
|
49 | suggest: options
|
50 | };
|
51 | }
|
52 |
|
53 | return {
|
54 | match,
|
55 | regex: new RegExp(match, 'gu'),
|
56 | fix: true,
|
57 | ...options
|
58 | };
|
59 | });
|
60 | }
|
61 |
|
62 | const create = context => {
|
63 | const {patterns} = {
|
64 | patterns: {},
|
65 | ...context.options[0]
|
66 | };
|
67 | const replacements = getReplacements(patterns);
|
68 |
|
69 | if (replacements.length === 0) {
|
70 | return {};
|
71 | }
|
72 |
|
73 | return {
|
74 | 'Literal, TemplateElement': node => {
|
75 | const {type} = node;
|
76 |
|
77 | let string;
|
78 | if (type === 'Literal') {
|
79 | string = node.value;
|
80 | } else if (!isIgnoredTag(node)) {
|
81 | string = node.value.raw;
|
82 | }
|
83 |
|
84 | if (!string || typeof string !== 'string') {
|
85 | return;
|
86 | }
|
87 |
|
88 | const replacement = replacements.find(({regex}) => regex.test(string));
|
89 |
|
90 | if (!replacement) {
|
91 | return;
|
92 | }
|
93 |
|
94 | const {fix: autoFix, message = defaultMessage, match, suggest} = replacement;
|
95 | const messageData = {
|
96 | match,
|
97 | suggest
|
98 | };
|
99 | const problem = {
|
100 | node,
|
101 | message,
|
102 | data: messageData
|
103 | };
|
104 |
|
105 | const fixed = string.replace(replacement.regex, suggest);
|
106 | const fix = type === 'Literal' ?
|
107 | fixer => fixer.replaceText(
|
108 | node,
|
109 | quoteString(fixed, node.raw[0])
|
110 | ) :
|
111 | fixer => replaceTemplateElement(
|
112 | fixer,
|
113 | node,
|
114 | escapeTemplateElementRaw(fixed)
|
115 | );
|
116 |
|
117 | if (autoFix) {
|
118 | problem.fix = fix;
|
119 | } else {
|
120 | problem.suggest = [
|
121 | {
|
122 | messageId: SUGGESTION_MESSAGE_ID,
|
123 | data: messageData,
|
124 | fix
|
125 | }
|
126 | ];
|
127 | }
|
128 |
|
129 | context.report(problem);
|
130 | }
|
131 | };
|
132 | };
|
133 |
|
134 | const schema = [
|
135 | {
|
136 | type: 'object',
|
137 | properties: {
|
138 | patterns: {
|
139 | type: 'object',
|
140 | additionalProperties: {
|
141 | anyOf: [
|
142 | {
|
143 | type: 'string'
|
144 | },
|
145 | {
|
146 | type: 'object',
|
147 | required: [
|
148 | 'suggest'
|
149 | ],
|
150 | properties: {
|
151 | suggest: {
|
152 | type: 'string'
|
153 | },
|
154 | fix: {
|
155 | type: 'boolean'
|
156 |
|
157 | },
|
158 | message: {
|
159 | type: 'string'
|
160 |
|
161 | }
|
162 | },
|
163 | additionalProperties: false
|
164 | }
|
165 | ]
|
166 | }}
|
167 | },
|
168 | additionalProperties: false
|
169 | }
|
170 | ];
|
171 |
|
172 | module.exports = {
|
173 | create,
|
174 | meta: {
|
175 | type: 'suggestion',
|
176 | docs: {
|
177 | url: getDocumentationUrl(__filename)
|
178 | },
|
179 | fixable: 'code',
|
180 | schema,
|
181 | messages: {
|
182 | [SUGGESTION_MESSAGE_ID]: 'Replace `{{match}}` with `{{suggest}}`.'
|
183 | }
|
184 | }
|
185 | };
|