1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | Object.defineProperty(exports, "__esModule", { value: true });
|
19 | exports.stringifyConfiguration = exports.isFileExcluded = exports.convertRuleOptions = exports.parseConfigFile = exports.getRulesDirectories = exports.useAsPath = exports.getRelativePath = exports.extendConfigurationFile = exports.readConfigurationFile = exports.loadConfigurationFromPath = exports.findConfigurationPath = exports.findConfiguration = exports.EMPTY_CONFIG = exports.DEFAULT_CONFIG = exports.CONFIG_FILENAMES = exports.CONFIG_FILENAME = exports.JSON_CONFIG_FILENAME = void 0;
|
20 | var tslib_1 = require("tslib");
|
21 | var fs = require("fs");
|
22 | var yaml = require("js-yaml");
|
23 | var minimatch_1 = require("minimatch");
|
24 | var os = require("os");
|
25 | var path = require("path");
|
26 | var error_1 = require("./error");
|
27 | var ruleLoader_1 = require("./ruleLoader");
|
28 | var utils_1 = require("./utils");
|
29 |
|
30 |
|
31 | exports.JSON_CONFIG_FILENAME = "tslint.json";
|
32 |
|
33 | exports.CONFIG_FILENAME = exports.JSON_CONFIG_FILENAME;
|
34 | exports.CONFIG_FILENAMES = [exports.JSON_CONFIG_FILENAME, "tslint.yaml", "tslint.yml"];
|
35 | exports.DEFAULT_CONFIG = {
|
36 | defaultSeverity: "error",
|
37 | extends: ["tslint:recommended"],
|
38 | jsRules: new Map(),
|
39 | rules: new Map(),
|
40 | rulesDirectory: [],
|
41 | };
|
42 | exports.EMPTY_CONFIG = {
|
43 | defaultSeverity: "error",
|
44 | extends: [],
|
45 | jsRules: new Map(),
|
46 | rules: new Map(),
|
47 | rulesDirectory: [],
|
48 | };
|
49 | var BUILT_IN_CONFIG = /^tslint:(.*)$/;
|
50 | function findConfiguration(configFile, inputFilePath) {
|
51 | var configPath = findConfigurationPath(configFile, inputFilePath);
|
52 | var loadResult = { path: configPath };
|
53 | try {
|
54 | loadResult.results = loadConfigurationFromPath(configPath);
|
55 | return loadResult;
|
56 | }
|
57 | catch (error) {
|
58 | throw new error_1.FatalError("Failed to load " + configPath + ": " + error.message, error);
|
59 | }
|
60 | }
|
61 | exports.findConfiguration = findConfiguration;
|
62 | function findConfigurationPath(suppliedConfigFilePath, inputFilePath) {
|
63 | if (suppliedConfigFilePath != undefined) {
|
64 | if (!fs.existsSync(suppliedConfigFilePath)) {
|
65 | throw new error_1.FatalError("Could not find config file at: " + path.resolve(suppliedConfigFilePath));
|
66 | }
|
67 | else {
|
68 | return path.resolve(suppliedConfigFilePath);
|
69 | }
|
70 | }
|
71 | else {
|
72 |
|
73 | var useDirName = false;
|
74 | try {
|
75 | var stats = fs.statSync(inputFilePath);
|
76 | if (stats.isFile()) {
|
77 | useDirName = true;
|
78 | }
|
79 | }
|
80 | catch (e) {
|
81 |
|
82 | useDirName = true;
|
83 | }
|
84 | if (useDirName) {
|
85 | inputFilePath = path.dirname(inputFilePath);
|
86 | }
|
87 |
|
88 | var configFilePath = findup(exports.CONFIG_FILENAMES, path.resolve(inputFilePath));
|
89 | if (configFilePath !== undefined) {
|
90 | return configFilePath;
|
91 | }
|
92 |
|
93 | var homeDir = os.homedir();
|
94 | for (var _i = 0, CONFIG_FILENAMES_1 = exports.CONFIG_FILENAMES; _i < CONFIG_FILENAMES_1.length; _i++) {
|
95 | var configFilename = CONFIG_FILENAMES_1[_i];
|
96 | configFilePath = path.join(homeDir, configFilename);
|
97 | if (fs.existsSync(configFilePath)) {
|
98 | return path.resolve(configFilePath);
|
99 | }
|
100 | }
|
101 |
|
102 | return undefined;
|
103 | }
|
104 | }
|
105 | exports.findConfigurationPath = findConfigurationPath;
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 | function findup(filenames, directory) {
|
112 | while (true) {
|
113 | var res = findFile(directory);
|
114 | if (res !== undefined) {
|
115 | return path.join(directory, res);
|
116 | }
|
117 | var parent = path.dirname(directory);
|
118 | if (parent === directory) {
|
119 | return undefined;
|
120 | }
|
121 | directory = parent;
|
122 | }
|
123 | function findFile(cwd) {
|
124 | var dirFiles = fs.readdirSync(cwd);
|
125 | var _loop_1 = function (filename) {
|
126 | var index = dirFiles.indexOf(filename);
|
127 | if (index > -1) {
|
128 | return { value: filename };
|
129 | }
|
130 |
|
131 |
|
132 | var result = dirFiles.find(function (entry) { return entry.toLowerCase() === filename; });
|
133 | if (result !== undefined) {
|
134 | error_1.showWarningOnce("Using mixed case " + filename + " is deprecated. Found: " + path.join(cwd, result));
|
135 | return { value: result };
|
136 | }
|
137 | };
|
138 | for (var _i = 0, filenames_1 = filenames; _i < filenames_1.length; _i++) {
|
139 | var filename = filenames_1[_i];
|
140 | var state_1 = _loop_1(filename);
|
141 | if (typeof state_1 === "object")
|
142 | return state_1.value;
|
143 | }
|
144 | return undefined;
|
145 | }
|
146 | }
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 | function loadConfigurationFromPath(configFilePath, _originalFilePath) {
|
158 | if (configFilePath == undefined) {
|
159 | return exports.DEFAULT_CONFIG;
|
160 | }
|
161 | else {
|
162 | var resolvedConfigFilePath = resolveConfigurationPath(configFilePath);
|
163 | var rawConfigFile = readConfigurationFile(resolvedConfigFilePath);
|
164 | return parseConfigFile(rawConfigFile, path.dirname(resolvedConfigFilePath), readConfigurationFile);
|
165 | }
|
166 | }
|
167 | exports.loadConfigurationFromPath = loadConfigurationFromPath;
|
168 |
|
169 | function readConfigurationFile(filepath) {
|
170 | var resolvedConfigFileExt = path.extname(filepath);
|
171 | if (/\.(json|ya?ml)/.test(resolvedConfigFileExt)) {
|
172 | var fileContent = fs.readFileSync(filepath, "utf8").replace(/^\uFEFF/, "");
|
173 | try {
|
174 | if (resolvedConfigFileExt === ".json") {
|
175 | return JSON.parse(utils_1.stripComments(fileContent));
|
176 | }
|
177 | else {
|
178 | return yaml.safeLoad(fileContent);
|
179 | }
|
180 | }
|
181 | catch (e) {
|
182 | var error = e;
|
183 |
|
184 | throw new Error(error.message + " in " + filepath);
|
185 | }
|
186 | }
|
187 | else {
|
188 | var rawConfigFile = require(filepath);
|
189 |
|
190 | delete require.cache[filepath];
|
191 | return rawConfigFile;
|
192 | }
|
193 | }
|
194 | exports.readConfigurationFile = readConfigurationFile;
|
195 |
|
196 |
|
197 |
|
198 |
|
199 | function resolveConfigurationPath(filePath, relativeTo) {
|
200 | var matches = filePath.match(BUILT_IN_CONFIG);
|
201 | var isBuiltInConfig = matches !== null && matches.length > 0;
|
202 | if (isBuiltInConfig) {
|
203 | var configName = matches[1];
|
204 | try {
|
205 | return require.resolve("./configs/" + configName);
|
206 | }
|
207 | catch (err) {
|
208 | throw new Error(filePath + " is not a built-in config, try \"tslint:recommended\" instead.");
|
209 | }
|
210 | }
|
211 | var basedir = relativeTo !== undefined ? relativeTo : process.cwd();
|
212 | try {
|
213 | var resolvedPackagePath = utils_1.tryResolvePackage(filePath, basedir);
|
214 | if (resolvedPackagePath === undefined) {
|
215 | resolvedPackagePath = require.resolve(filePath);
|
216 | }
|
217 | return resolvedPackagePath;
|
218 | }
|
219 | catch (err) {
|
220 | throw new Error("Invalid \"extends\" configuration value - could not require \"" + filePath + "\". " +
|
221 | "Review the Node lookup algorithm (https://nodejs.org/api/modules.html#modules_all_together) " +
|
222 | "for the approximate method TSLint uses to find the referenced configuration file.");
|
223 | }
|
224 | }
|
225 | function extendConfigurationFile(targetConfig, nextConfigSource) {
|
226 | function combineProperties(targetProperty, nextProperty) {
|
227 | var combinedProperty = {};
|
228 | add(targetProperty);
|
229 |
|
230 | add(nextProperty);
|
231 | return combinedProperty;
|
232 | function add(property) {
|
233 | if (property !== undefined) {
|
234 | for (var _i = 0, _a = Object.keys(property); _i < _a.length; _i++) {
|
235 | var name = _a[_i];
|
236 | combinedProperty[name] = property[name];
|
237 | }
|
238 | }
|
239 | }
|
240 | }
|
241 | function combineMaps(target, next) {
|
242 | var combined = new Map();
|
243 | target.forEach(function (options, ruleName) {
|
244 | combined.set(ruleName, options);
|
245 | });
|
246 | next.forEach(function (options, ruleName) {
|
247 | var combinedRule = combined.get(ruleName);
|
248 | if (combinedRule !== undefined) {
|
249 | combined.set(ruleName, combineProperties(combinedRule, options));
|
250 | }
|
251 | else {
|
252 | combined.set(ruleName, options);
|
253 | }
|
254 | });
|
255 | return combined;
|
256 | }
|
257 | var combinedRulesDirs = targetConfig.rulesDirectory.concat(nextConfigSource.rulesDirectory);
|
258 | var dedupedRulesDirs = Array.from(new Set(combinedRulesDirs));
|
259 | return {
|
260 | extends: [],
|
261 | jsRules: combineMaps(targetConfig.jsRules, nextConfigSource.jsRules),
|
262 | linterOptions: combineProperties(targetConfig.linterOptions, nextConfigSource.linterOptions),
|
263 | rules: combineMaps(targetConfig.rules, nextConfigSource.rules),
|
264 | rulesDirectory: dedupedRulesDirs,
|
265 | };
|
266 | }
|
267 | exports.extendConfigurationFile = extendConfigurationFile;
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 |
|
274 | function getRelativePath(directory, relativeTo) {
|
275 | if (directory != undefined) {
|
276 | var basePath = relativeTo !== undefined ? relativeTo : process.cwd();
|
277 | return path.resolve(basePath, directory);
|
278 | }
|
279 | return undefined;
|
280 | }
|
281 | exports.getRelativePath = getRelativePath;
|
282 |
|
283 |
|
284 | function useAsPath(directory) {
|
285 | return /^(?:\.?\.?(?:\/|$)|node_modules\/)/.test(directory);
|
286 | }
|
287 | exports.useAsPath = useAsPath;
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 | function getRulesDirectories(directories, relativeTo) {
|
296 | return utils_1.arrayify(directories).map(function (dir) {
|
297 | if (!useAsPath(dir)) {
|
298 | var resolvedPackagePath = utils_1.tryResolvePackage(dir, relativeTo);
|
299 | if (resolvedPackagePath !== undefined) {
|
300 | return path.dirname(resolvedPackagePath);
|
301 | }
|
302 | }
|
303 | var absolutePath = relativeTo === undefined ? path.resolve(dir) : path.resolve(relativeTo, dir);
|
304 | if (absolutePath !== undefined) {
|
305 | if (!fs.existsSync(absolutePath)) {
|
306 | throw new error_1.FatalError("Could not find custom rule directory: " + dir);
|
307 | }
|
308 | }
|
309 | return absolutePath;
|
310 | });
|
311 | }
|
312 | exports.getRulesDirectories = getRulesDirectories;
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 | function parseRuleOptions(ruleConfigValue, rawDefaultRuleSeverity) {
|
319 | var ruleArguments;
|
320 | var defaultRuleSeverity = "error";
|
321 | if (rawDefaultRuleSeverity !== undefined) {
|
322 | switch (rawDefaultRuleSeverity.toLowerCase()) {
|
323 | case "warn":
|
324 | case "warning":
|
325 | defaultRuleSeverity = "warning";
|
326 | break;
|
327 | case "off":
|
328 | case "none":
|
329 | defaultRuleSeverity = "off";
|
330 | break;
|
331 | default:
|
332 | defaultRuleSeverity = "error";
|
333 | }
|
334 | }
|
335 | var ruleSeverity = defaultRuleSeverity;
|
336 | if (ruleConfigValue == undefined) {
|
337 | ruleArguments = [];
|
338 | ruleSeverity = "off";
|
339 | }
|
340 | else if (Array.isArray(ruleConfigValue)) {
|
341 | if (ruleConfigValue.length > 0) {
|
342 |
|
343 | ruleArguments = ruleConfigValue.slice(1);
|
344 | ruleSeverity = ruleConfigValue[0] === true ? defaultRuleSeverity : "off";
|
345 | }
|
346 | }
|
347 | else if (typeof ruleConfigValue === "boolean") {
|
348 |
|
349 | ruleArguments = [];
|
350 | ruleSeverity = ruleConfigValue ? defaultRuleSeverity : "off";
|
351 | }
|
352 | else if (typeof ruleConfigValue === "object") {
|
353 | if (ruleConfigValue.severity !== undefined) {
|
354 | switch (ruleConfigValue.severity.toLowerCase()) {
|
355 | case "default":
|
356 | ruleSeverity = defaultRuleSeverity;
|
357 | break;
|
358 | case "error":
|
359 | ruleSeverity = "error";
|
360 | break;
|
361 | case "warn":
|
362 | case "warning":
|
363 | ruleSeverity = "warning";
|
364 | break;
|
365 | case "off":
|
366 | case "none":
|
367 | ruleSeverity = "off";
|
368 | break;
|
369 | default:
|
370 | console.warn("Invalid severity level: " + ruleConfigValue.severity);
|
371 | ruleSeverity = defaultRuleSeverity;
|
372 | }
|
373 | }
|
374 | if (ruleConfigValue.options != undefined) {
|
375 | ruleArguments = utils_1.arrayify(ruleConfigValue.options);
|
376 | }
|
377 | }
|
378 | return {
|
379 | ruleArguments: ruleArguments,
|
380 | ruleSeverity: ruleSeverity,
|
381 | };
|
382 | }
|
383 |
|
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 | function parseConfigFile(configFile, configFileDir, readConfig) {
|
392 | var defaultSeverity = configFile.defaultSeverity;
|
393 | if (readConfig === undefined || configFileDir === undefined) {
|
394 | return parse(configFile, configFileDir);
|
395 | }
|
396 | return loadExtendsRecursive(configFile, configFileDir)
|
397 | .map(function (_a) {
|
398 | var dir = _a.dir, config = _a.config;
|
399 | return parse(config, dir);
|
400 | })
|
401 | .reduce(extendConfigurationFile, exports.EMPTY_CONFIG);
|
402 |
|
403 | function loadExtendsRecursive(raw, dir) {
|
404 | var configs = [];
|
405 | for (var _i = 0, _a = utils_1.arrayify(raw.extends); _i < _a.length; _i++) {
|
406 | var relativePath = _a[_i];
|
407 | var resolvedPath = resolveConfigurationPath(relativePath, dir);
|
408 | var extendedRaw = readConfig(resolvedPath);
|
409 | configs.push.apply(configs, loadExtendsRecursive(extendedRaw, path.dirname(resolvedPath)));
|
410 | }
|
411 | if (raw.defaultSeverity !== undefined) {
|
412 | defaultSeverity = raw.defaultSeverity;
|
413 | }
|
414 | configs.push({ dir: dir, config: raw });
|
415 | return configs;
|
416 | }
|
417 | function parse(config, dir) {
|
418 | var rulesDirectory = getRulesDirectories(config.rulesDirectory, dir);
|
419 | var rules = parseRules(config.rules);
|
420 | var jsRules = typeof config.jsRules === "boolean"
|
421 | ? filterValidJsRules(rules, config.jsRules, rulesDirectory)
|
422 | : parseRules(config.jsRules);
|
423 | return {
|
424 | extends: utils_1.arrayify(config.extends),
|
425 | jsRules: jsRules,
|
426 | linterOptions: parseLinterOptions(config.linterOptions, dir),
|
427 | rules: rules,
|
428 | rulesDirectory: rulesDirectory,
|
429 | };
|
430 | }
|
431 | function parseRules(config) {
|
432 | var map = new Map();
|
433 | if (config !== undefined) {
|
434 | for (var _i = 0, _a = Object.keys(config); _i < _a.length; _i++) {
|
435 | var ruleName = _a[_i];
|
436 | map.set(ruleName, parseRuleOptions(config[ruleName], defaultSeverity));
|
437 | }
|
438 | }
|
439 | return map;
|
440 | }
|
441 | function filterValidJsRules(rules, copyRulestoJsRules, rulesDirectory) {
|
442 | if (copyRulestoJsRules === void 0) { copyRulestoJsRules = false; }
|
443 | var validJsRules = new Map();
|
444 | if (copyRulestoJsRules) {
|
445 | rules.forEach(function (ruleOptions, ruleName) {
|
446 | var Rule = ruleLoader_1.findRule(ruleName, rulesDirectory);
|
447 | if (Rule !== undefined &&
|
448 | (Rule.metadata === undefined || !Rule.metadata.typescriptOnly)) {
|
449 | validJsRules.set(ruleName, ruleOptions);
|
450 | }
|
451 | });
|
452 | }
|
453 | return validJsRules;
|
454 | }
|
455 | function parseLinterOptions(raw, dir) {
|
456 | if (raw === undefined) {
|
457 | return {};
|
458 | }
|
459 | return tslib_1.__assign(tslib_1.__assign({}, (raw.exclude !== undefined
|
460 | ? {
|
461 | exclude: utils_1.arrayify(raw.exclude).map(function (pattern) {
|
462 | return dir === undefined ? path.resolve(pattern) : path.resolve(dir, pattern);
|
463 | }),
|
464 | }
|
465 | : {})), (raw.format !== undefined
|
466 | ? {
|
467 | format: raw.format,
|
468 | }
|
469 | : {}));
|
470 | }
|
471 | }
|
472 | exports.parseConfigFile = parseConfigFile;
|
473 |
|
474 |
|
475 |
|
476 | function convertRuleOptions(ruleConfiguration) {
|
477 | var output = [];
|
478 | ruleConfiguration.forEach(function (_a, ruleName) {
|
479 | var ruleArguments = _a.ruleArguments, ruleSeverity = _a.ruleSeverity;
|
480 | var options = {
|
481 | disabledIntervals: [],
|
482 | ruleArguments: ruleArguments != undefined ? ruleArguments : [],
|
483 | ruleName: ruleName,
|
484 | ruleSeverity: ruleSeverity != undefined ? ruleSeverity : "error",
|
485 | };
|
486 | output.push(options);
|
487 | });
|
488 | return output;
|
489 | }
|
490 | exports.convertRuleOptions = convertRuleOptions;
|
491 | function isFileExcluded(filepath, configFile) {
|
492 | if (configFile === undefined ||
|
493 | configFile.linterOptions == undefined ||
|
494 | configFile.linterOptions.exclude == undefined) {
|
495 | return false;
|
496 | }
|
497 | var fullPath = path.resolve(filepath);
|
498 | return configFile.linterOptions.exclude.some(function (pattern) { return new minimatch_1.Minimatch(pattern).match(fullPath); });
|
499 | }
|
500 | exports.isFileExcluded = isFileExcluded;
|
501 | function stringifyConfiguration(configFile) {
|
502 | return JSON.stringify({
|
503 | extends: configFile.extends,
|
504 | jsRules: convertRulesMapToObject(configFile.jsRules),
|
505 | linterOptions: configFile.linterOptions,
|
506 | rules: convertRulesMapToObject(configFile.rules),
|
507 | rulesDirectory: configFile.rulesDirectory,
|
508 | }, undefined, 2);
|
509 | }
|
510 | exports.stringifyConfiguration = stringifyConfiguration;
|
511 | function convertRulesMapToObject(rules) {
|
512 | return Array.from(rules).reduce(function (result, _a) {
|
513 | var _b;
|
514 | var key = _a[0], value = _a[1];
|
515 | return (tslib_1.__assign(tslib_1.__assign({}, result), (_b = {}, _b[key] = value, _b)));
|
516 | }, {});
|
517 | }
|