.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / tslint / lib / rules / quotemarkRule.js
1 "use strict";
2 /**
3  * @license
4  * Copyright 2013 Palantir Technologies, Inc.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 Object.defineProperty(exports, "__esModule", { value: true });
19 var tslib_1 = require("tslib");
20 var semver_1 = require("semver");
21 var tsutils_1 = require("tsutils");
22 var ts = require("typescript");
23 var Lint = require("../index");
24 var parse_1 = require("../verify/parse");
25 var OPTION_SINGLE = "single";
26 var OPTION_DOUBLE = "double";
27 var OPTION_BACKTICK = "backtick";
28 var OPTION_JSX_SINGLE = "jsx-single";
29 var OPTION_JSX_DOUBLE = "jsx-double";
30 var OPTION_AVOID_TEMPLATE = "avoid-template";
31 var OPTION_AVOID_ESCAPE = "avoid-escape";
32 function isQuoteMark(value) {
33     return ["'", '"', "`"].indexOf(value) > -1;
34 }
35 var Rule = /** @class */ (function (_super) {
36     tslib_1.__extends(Rule, _super);
37     function Rule() {
38         return _super !== null && _super.apply(this, arguments) || this;
39     }
40     /* tslint:enable:object-literal-sort-keys */
41     Rule.FAILURE_STRING = function (actual, expected) {
42         return actual + " should be " + expected;
43     };
44     Rule.prototype.apply = function (sourceFile) {
45         var args = this.ruleArguments;
46         var quotemark = getQuotemarkPreference(args);
47         var jsxQuotemark = getJSXQuotemarkPreference(args, quotemark);
48         return this.applyWithFunction(sourceFile, walk, {
49             avoidEscape: hasArg(OPTION_AVOID_ESCAPE),
50             avoidTemplate: hasArg(OPTION_AVOID_TEMPLATE),
51             jsxQuotemark: jsxQuotemark,
52             quotemark: quotemark,
53         });
54         function hasArg(name) {
55             return args.indexOf(name) !== -1;
56         }
57     };
58     /* tslint:disable:object-literal-sort-keys */
59     Rule.metadata = {
60         ruleName: "quotemark",
61         description: "Enforces quote character for string literals.",
62         hasFix: true,
63         optionsDescription: Lint.Utils.dedent(templateObject_1 || (templateObject_1 = tslib_1.__makeTemplateObject(["\n            Five arguments may be optionally provided:\n\n            * `\"", "\"` enforces single quotes.\n            * `\"", "\"` enforces double quotes.\n            * `\"", "\"` enforces backticks.\n            * `\"", "\"` enforces single quotes for JSX attributes.\n            * `\"", "\"` enforces double quotes for JSX attributes.\n            * `\"", "\"` forbids single-line untagged template strings that do not contain string interpolations.\n                Note that backticks may still be used if `\"", "\"` is enabled and both single and double quotes are\n                present in the string (the latter option takes precedence).\n            * `\"", "\"` allows you to use the \"other\" quotemark in cases where escaping would normally be required.\n                For example, `[true, \"", "\", \"", "\"]` would not report a failure on the string literal\n                `'Hello \"World\"'`."], ["\n            Five arguments may be optionally provided:\n\n            * \\`\"", "\"\\` enforces single quotes.\n            * \\`\"", "\"\\` enforces double quotes.\n            * \\`\"", "\"\\` enforces backticks.\n            * \\`\"", "\"\\` enforces single quotes for JSX attributes.\n            * \\`\"", "\"\\` enforces double quotes for JSX attributes.\n            * \\`\"", "\"\\` forbids single-line untagged template strings that do not contain string interpolations.\n                Note that backticks may still be used if \\`\"", "\"\\` is enabled and both single and double quotes are\n                present in the string (the latter option takes precedence).\n            * \\`\"", "\"\\` allows you to use the \"other\" quotemark in cases where escaping would normally be required.\n                For example, \\`[true, \"", "\", \"", "\"]\\` would not report a failure on the string literal\n                \\`'Hello \"World\"'\\`."])), OPTION_SINGLE, OPTION_DOUBLE, OPTION_BACKTICK, OPTION_JSX_SINGLE, OPTION_JSX_DOUBLE, OPTION_AVOID_TEMPLATE, OPTION_AVOID_ESCAPE, OPTION_AVOID_ESCAPE, OPTION_DOUBLE, OPTION_AVOID_ESCAPE),
64         options: {
65             type: "array",
66             items: {
67                 type: "string",
68                 enum: [
69                     OPTION_SINGLE,
70                     OPTION_DOUBLE,
71                     OPTION_BACKTICK,
72                     OPTION_JSX_SINGLE,
73                     OPTION_JSX_DOUBLE,
74                     OPTION_AVOID_ESCAPE,
75                     OPTION_AVOID_TEMPLATE,
76                 ],
77             },
78             minLength: 0,
79             maxLength: 5,
80         },
81         optionExamples: [
82             [true, OPTION_SINGLE, OPTION_AVOID_ESCAPE, OPTION_AVOID_TEMPLATE],
83             [true, OPTION_SINGLE, OPTION_JSX_DOUBLE],
84         ],
85         type: "formatting",
86         typescriptOnly: false,
87     };
88     return Rule;
89 }(Lint.Rules.AbstractRule));
90 exports.Rule = Rule;
91 function walk(ctx) {
92     var sourceFile = ctx.sourceFile, options = ctx.options;
93     ts.forEachChild(sourceFile, function cb(node) {
94         if (tsutils_1.isStringLiteral(node) ||
95             (options.avoidTemplate &&
96                 tsutils_1.isNoSubstitutionTemplateLiteral(node) &&
97                 node.parent.kind !== ts.SyntaxKind.TaggedTemplateExpression &&
98                 tsutils_1.isSameLine(sourceFile, node.getStart(sourceFile), node.end))) {
99             var expectedQuotemark = node.parent.kind === ts.SyntaxKind.JsxAttribute
100                 ? options.jsxQuotemark
101                 : options.quotemark;
102             var actualQuotemark = sourceFile.text[node.end - 1];
103             // Don't use backticks instead of single/double quotes when it breaks TypeScript syntax.
104             if (expectedQuotemark === "`" && isNotValidToUseBackticksInNode(node, sourceFile)) {
105                 return;
106             }
107             // We already have the expected quotemark, or the quotemark is invalid. Done.
108             if (actualQuotemark === expectedQuotemark || !isQuoteMark(actualQuotemark)) {
109                 return;
110             }
111             /** The quotemark we intend to use to fix this node. */
112             var fixQuotemark = expectedQuotemark;
113             /**
114              * Whether this node needs to be escaped (because
115              *   it contains the expected quotemark).
116              */
117             var needsToBeEscaped = node.text.includes(expectedQuotemark);
118             // This string requires escapes to use the expected quote mark, but `avoid-escape` was passed
119             if (needsToBeEscaped && options.avoidEscape) {
120                 if (node.kind === ts.SyntaxKind.StringLiteral) {
121                     return;
122                 }
123                 // If we are expecting double quotes, use single quotes to avoid escaping.
124                 // Otherwise, just use double quotes.
125                 var alternativeFixQuotemark = expectedQuotemark === '"' ? "'" : '"';
126                 if (node.text.includes(alternativeFixQuotemark)) {
127                     // It also includes the alternative fix quotemark. Let's try to use single quotes instead,
128                     // unless we originally expected single quotes, in which case we will try to use backticks.
129                     // This means that we may use backtick even with avoid-template in trying to avoid escaping.
130                     fixQuotemark = expectedQuotemark === "'" ? "`" : "'";
131                     if (fixQuotemark === actualQuotemark) {
132                         // We were already using the best quote mark for this scenario
133                         return;
134                     }
135                     else if (node.text.includes(fixQuotemark)) {
136                         // It contains all of the other kinds of quotes. Escaping is unavoidable, sadly.
137                         return;
138                     }
139                 }
140                 else {
141                     fixQuotemark = alternativeFixQuotemark;
142                 }
143             }
144             var start = node.getStart(sourceFile);
145             var text = sourceFile.text.substring(start + 1, node.end - 1);
146             if (needsToBeEscaped) {
147                 text = text.replace(new RegExp(fixQuotemark, "g"), "\\" + fixQuotemark);
148             }
149             text = text.replace(new RegExp("\\\\" + actualQuotemark, "g"), actualQuotemark);
150             return ctx.addFailure(start, node.end, Rule.FAILURE_STRING(actualQuotemark, fixQuotemark), new Lint.Replacement(start, node.end - start, fixQuotemark + text + fixQuotemark));
151         }
152         ts.forEachChild(node, cb);
153     });
154 }
155 function getQuotemarkPreference(ruleArguments) {
156     for (var _i = 0, ruleArguments_1 = ruleArguments; _i < ruleArguments_1.length; _i++) {
157         var arg = ruleArguments_1[_i];
158         switch (arg) {
159             case OPTION_SINGLE:
160                 return "'";
161             case OPTION_DOUBLE:
162                 return '"';
163             case OPTION_BACKTICK:
164                 return "`";
165             default:
166                 continue;
167         }
168     }
169     // Default to double quotes if no pref is found.
170     return '"';
171 }
172 function getJSXQuotemarkPreference(ruleArguments, regularQuotemarkPreference) {
173     for (var _i = 0, ruleArguments_2 = ruleArguments; _i < ruleArguments_2.length; _i++) {
174         var arg = ruleArguments_2[_i];
175         switch (arg) {
176             case OPTION_JSX_SINGLE:
177                 return "'";
178             case OPTION_JSX_DOUBLE:
179                 return '"';
180             default:
181                 continue;
182         }
183     }
184     // The JSX preference was not found, so try to use the regular preference.
185     //   If the regular pref is backtick, use double quotes instead.
186     return regularQuotemarkPreference !== "`" ? regularQuotemarkPreference : '"';
187 }
188 function isNotValidToUseBackticksInNode(node, sourceFile) {
189     return (
190     // This captures `export blah from "package"`
191     tsutils_1.isExportDeclaration(node.parent) ||
192         // This captures `import blah from "package"`
193         tsutils_1.isImportDeclaration(node.parent) ||
194         // This captures quoted names in object literal keys
195         isNameInAssignment(node) ||
196         // This captures quoted signatures (property or method)
197         isSignature(node) ||
198         // This captures literal types in generic type constraints
199         isTypeConstraint(node) ||
200         // Older TS doesn't narrow a type when backticks are used to compare typeof
201         isTypeCheckWithOldTsc(node) ||
202         // Enum members can't use backticks
203         tsutils_1.isEnumMember(node.parent) ||
204         // Typescript converts old octal escape sequences to just the numbers therein
205         containsOctalEscapeSequence(node, sourceFile) ||
206         // Use strict declarations have to be single or double quoted
207         isUseStrictDeclaration(node) ||
208         // Lookup type parameters must be single/double quoted
209         isLookupTypeParameter(node));
210 }
211 /**
212  * Whether this node is a type constraint in a generic type.
213  * @param  node The node to check
214  * @return Whether this node is a type constraint
215  */
216 function isTypeConstraint(node) {
217     var parent = node.parent.parent;
218     // If this node doesn't have a grandparent, it's not a type constraint
219     if (parent == undefined) {
220         return false;
221     }
222     // Iterate through all levels of union, intersection, or parethesized types
223     while (parent.kind === ts.SyntaxKind.UnionType ||
224         parent.kind === ts.SyntaxKind.IntersectionType ||
225         parent.kind === ts.SyntaxKind.ParenthesizedType) {
226         parent = parent.parent;
227     }
228     return (
229     // If the next level is a type reference, the node is a type constraint
230     parent.kind === ts.SyntaxKind.TypeReference ||
231         // If the next level is a type parameter, the node is a type constraint
232         parent.kind === ts.SyntaxKind.TypeParameter);
233 }
234 /**
235  * Whether this node is the signature of a property or method in a type.
236  * @param  node The node to check
237  * @return Whether this node is a property/method signature.
238  */
239 function isSignature(node) {
240     var parent = node.parent;
241     if (hasOldTscBacktickBehavior() && node.parent.kind === ts.SyntaxKind.LastTypeNode) {
242         // In older versions, there's a "LastTypeNode" here
243         parent = parent.parent;
244     }
245     return (
246     // This captures the kebab-case property names in type definitions
247     parent.kind === ts.SyntaxKind.PropertySignature ||
248         // This captures the kebab-case method names in type definitions
249         parent.kind === ts.SyntaxKind.MethodSignature);
250 }
251 /**
252  * Whether this node is the method or property name in an assignment/declaration.
253  * @param  node The node to check
254  * @return Whether this node is the name in an assignment/decleration.
255  */
256 function isNameInAssignment(node) {
257     if (node.parent.kind !== ts.SyntaxKind.PropertyAssignment &&
258         node.parent.kind !== ts.SyntaxKind.MethodDeclaration) {
259         // If the node is neither a property assignment or method declaration, it's not a name in an assignment
260         return false;
261     }
262     return (
263     // In old typescript versions, don't change values either
264     hasOldTscBacktickBehavior() ||
265         // If this node is not at the end of the parent
266         node.end !== node.parent.end);
267 }
268 function isTypeCheckWithOldTsc(node) {
269     if (!hasOldTscBacktickBehavior()) {
270         // This one only affects older typescript versions
271         return false;
272     }
273     if (node.parent.kind !== ts.SyntaxKind.BinaryExpression) {
274         // If this isn't in a binary expression
275         return false;
276     }
277     // If this node has a sibling that is a TypeOf
278     return node.parent.getChildren().some(function (n) { return n.kind === ts.SyntaxKind.TypeOfExpression; });
279 }
280 function containsOctalEscapeSequence(node, sourceFile) {
281     // Octal sequences can go from 1-377 (255 in octal), but let's match the prefix, which will at least be \1-\77
282     // Using node.getText here strips the backslashes from the string. We also need to make sure there isn't an even
283     // number of backslashes (then it would not be an escape sequence, but a literal backslash followed by numbers).
284     var matches = node.getText(sourceFile).match(/(\\)+[1-7][0-7]?/g);
285     if (matches != undefined) {
286         for (var _i = 0, matches_1 = matches; _i < matches_1.length; _i++) {
287             var match = matches_1[_i];
288             var numBackslashes = match.match(/\\/g).length;
289             if (numBackslashes % 2 === 1) {
290                 // There was an odd number of backslashes preceeding this node – it's an octal escape sequence
291                 return true;
292             }
293         }
294     }
295     return false;
296 }
297 function isUseStrictDeclaration(node) {
298     return node.text === "use strict" && tsutils_1.isExpressionStatement(node.parent);
299 }
300 function isLookupTypeParameter(node) {
301     return tsutils_1.isLiteralTypeNode(node.parent) && tsutils_1.isIndexedAccessTypeNode(node.parent.parent);
302 }
303 /** Versions of typescript below 2.7.1 treat backticks differently */
304 function hasOldTscBacktickBehavior() {
305     return semver_1.lt(parse_1.getNormalizedTypescriptVersion(), "2.7.1");
306 }
307 var templateObject_1;