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