225 lines
9.5 KiB
JavaScript
225 lines
9.5 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2013 Palantir Technologies, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
var findup = require("findup-sync");
|
|
var fs = require("fs");
|
|
var path = require("path");
|
|
var resolve = require("resolve");
|
|
var error_1 = require("./error");
|
|
var utils_1 = require("./utils");
|
|
exports.CONFIG_FILENAME = "tslint.json";
|
|
/* tslint:disable:object-literal-key-quotes */
|
|
exports.DEFAULT_CONFIG = {
|
|
"extends": "tslint:recommended",
|
|
};
|
|
/* tslint:enable:object-literal-key-quotes */
|
|
var BUILT_IN_CONFIG = /^tslint:(.*)$/;
|
|
/**
|
|
* Searches for a TSLint configuration and returns the data from the config.
|
|
* @param configFile A path to a config file, this can be null if the location of a config is not known
|
|
* @param inputFileLocation A path to the current file being linted. This is the starting location
|
|
* of the search for a configuration.
|
|
* @returns Load status for a TSLint configuration object
|
|
*/
|
|
function findConfiguration(configFile, inputFilePath) {
|
|
var path = findConfigurationPath(configFile, inputFilePath);
|
|
var loadResult = { path: path };
|
|
try {
|
|
loadResult.results = loadConfigurationFromPath(path);
|
|
return loadResult;
|
|
}
|
|
catch (error) {
|
|
throw new error_1.FatalError("Failed to load " + path + ": " + error.message, error);
|
|
}
|
|
}
|
|
exports.findConfiguration = findConfiguration;
|
|
/**
|
|
* Searches for a TSLint configuration and returns the path to it.
|
|
* Could return undefined if not configuration is found.
|
|
* @param suppliedConfigFilePath A path to an known config file supplied by a user. Pass null here if
|
|
* the location of the config file is not known and you want to search for one.
|
|
* @param inputFilePath A path to the current file being linted. This is the starting location
|
|
* of the search for a configuration.
|
|
* @returns An absolute path to a tslint.json file
|
|
* or undefined if neither can be found.
|
|
*/
|
|
function findConfigurationPath(suppliedConfigFilePath, inputFilePath) {
|
|
if (suppliedConfigFilePath != null) {
|
|
if (!fs.existsSync(suppliedConfigFilePath)) {
|
|
throw new Error("Could not find config file at: " + path.resolve(suppliedConfigFilePath));
|
|
}
|
|
else {
|
|
return path.resolve(suppliedConfigFilePath);
|
|
}
|
|
}
|
|
else {
|
|
// search for tslint.json from input file location
|
|
var configFilePath = findup(exports.CONFIG_FILENAME, { cwd: inputFilePath, nocase: true });
|
|
if (configFilePath != null && fs.existsSync(configFilePath)) {
|
|
return path.resolve(configFilePath);
|
|
}
|
|
// search for tslint.json in home directory
|
|
var homeDir = getHomeDir();
|
|
if (homeDir != null) {
|
|
configFilePath = path.join(homeDir, exports.CONFIG_FILENAME);
|
|
if (fs.existsSync(configFilePath)) {
|
|
return path.resolve(configFilePath);
|
|
}
|
|
}
|
|
// no path could be found
|
|
return undefined;
|
|
}
|
|
}
|
|
exports.findConfigurationPath = findConfigurationPath;
|
|
/**
|
|
* Used Node semantics to load a configuration file given configFilePath.
|
|
* For example:
|
|
* '/path/to/config' will be treated as an absolute path
|
|
* './path/to/config' will be treated as a relative path
|
|
* 'path/to/config' will attempt to load a to/config file inside a node module named path
|
|
* @returns a configuration object for TSLint loaded from the file at configFilePath
|
|
*/
|
|
function loadConfigurationFromPath(configFilePath) {
|
|
if (configFilePath == null) {
|
|
return exports.DEFAULT_CONFIG;
|
|
}
|
|
else {
|
|
var resolvedConfigFilePath = resolveConfigurationPath(configFilePath);
|
|
var configFile = void 0;
|
|
if (path.extname(resolvedConfigFilePath) === ".json") {
|
|
var fileContent = utils_1.stripComments(fs.readFileSync(resolvedConfigFilePath)
|
|
.toString()
|
|
.replace(/^\uFEFF/, ""));
|
|
configFile = JSON.parse(fileContent);
|
|
}
|
|
else {
|
|
configFile = require(resolvedConfigFilePath);
|
|
delete require.cache[resolvedConfigFilePath];
|
|
}
|
|
var configFileDir_1 = path.dirname(resolvedConfigFilePath);
|
|
configFile.rulesDirectory = getRulesDirectories(configFile.rulesDirectory, configFileDir_1);
|
|
// load configurations, in order, using their identifiers or relative paths
|
|
// apply the current configuration last by placing it last in this array
|
|
var configs = utils_1.arrayify(configFile.extends).map(function (name) {
|
|
var nextConfigFilePath = resolveConfigurationPath(name, configFileDir_1);
|
|
return loadConfigurationFromPath(nextConfigFilePath);
|
|
}).concat([configFile]);
|
|
return configs.reduce(extendConfigurationFile, {});
|
|
}
|
|
}
|
|
exports.loadConfigurationFromPath = loadConfigurationFromPath;
|
|
/**
|
|
* Resolve configuration file path or node_module reference
|
|
* @param filePath Relative ("./path"), absolute ("/path"), node module ("path"), or built-in ("tslint:path")
|
|
*/
|
|
function resolveConfigurationPath(filePath, relativeTo) {
|
|
var matches = filePath.match(BUILT_IN_CONFIG);
|
|
var isBuiltInConfig = matches != null && matches.length > 0;
|
|
if (isBuiltInConfig) {
|
|
var configName = matches[1];
|
|
try {
|
|
return require.resolve("./configs/" + configName);
|
|
}
|
|
catch (err) {
|
|
throw new Error(filePath + " is not a built-in config, try \"tslint:recommended\" instead.");
|
|
}
|
|
}
|
|
var basedir = relativeTo || process.cwd();
|
|
try {
|
|
return resolve.sync(filePath, { basedir: basedir });
|
|
}
|
|
catch (err) {
|
|
try {
|
|
return require.resolve(filePath);
|
|
}
|
|
catch (err) {
|
|
throw new Error("Invalid \"extends\" configuration value - could not require \"" + filePath + "\". " +
|
|
"Review the Node lookup algorithm (https://nodejs.org/api/modules.html#modules_all_together) " +
|
|
"for the approximate method TSLint uses to find the referenced configuration file.");
|
|
}
|
|
}
|
|
}
|
|
function extendConfigurationFile(targetConfig, nextConfigSource) {
|
|
var combinedConfig = {};
|
|
var configRulesDirectory = utils_1.arrayify(targetConfig.rulesDirectory);
|
|
var nextConfigRulesDirectory = utils_1.arrayify(nextConfigSource.rulesDirectory);
|
|
combinedConfig.rulesDirectory = configRulesDirectory.concat(nextConfigRulesDirectory);
|
|
var combineProperties = function (targetProperty, nextProperty) {
|
|
var combinedProperty = {};
|
|
for (var _i = 0, _a = Object.keys(utils_1.objectify(targetProperty)); _i < _a.length; _i++) {
|
|
var name = _a[_i];
|
|
combinedProperty[name] = targetProperty[name];
|
|
}
|
|
// next config source overwrites the target config object
|
|
for (var _b = 0, _c = Object.keys(utils_1.objectify(nextProperty)); _b < _c.length; _b++) {
|
|
var name = _c[_b];
|
|
combinedProperty[name] = nextProperty[name];
|
|
}
|
|
return combinedProperty;
|
|
};
|
|
combinedConfig.rules = combineProperties(targetConfig.rules, nextConfigSource.rules);
|
|
combinedConfig.jsRules = combineProperties(targetConfig.jsRules, nextConfigSource.jsRules);
|
|
combinedConfig.linterOptions = combineProperties(targetConfig.linterOptions, nextConfigSource.linterOptions);
|
|
return combinedConfig;
|
|
}
|
|
exports.extendConfigurationFile = extendConfigurationFile;
|
|
function getHomeDir() {
|
|
var environment = global.process.env;
|
|
var paths = [
|
|
environment.USERPROFILE,
|
|
environment.HOME,
|
|
environment.HOMEPATH,
|
|
environment.HOMEDRIVE + environment.HOMEPATH,
|
|
];
|
|
for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) {
|
|
var homePath = paths_1[_i];
|
|
if (homePath != null && fs.existsSync(homePath)) {
|
|
return homePath;
|
|
}
|
|
}
|
|
}
|
|
// returns the absolute path (contrary to what the name implies)
|
|
function getRelativePath(directory, relativeTo) {
|
|
if (directory != null) {
|
|
var basePath = relativeTo || process.cwd();
|
|
return path.resolve(basePath, directory);
|
|
}
|
|
return undefined;
|
|
}
|
|
exports.getRelativePath = getRelativePath;
|
|
/**
|
|
* @param directories A path(s) to a directory of custom rules
|
|
* @param relativeTo A path that directories provided are relative to.
|
|
* For example, if the directories come from a tslint.json file, this path
|
|
* should be the path to the tslint.json file.
|
|
* @return An array of absolute paths to directories potentially containing rules
|
|
*/
|
|
function getRulesDirectories(directories, relativeTo) {
|
|
var rulesDirectories = utils_1.arrayify(directories)
|
|
.map(function (dir) { return getRelativePath(dir, relativeTo); })
|
|
.filter(function (dir) { return dir !== undefined; });
|
|
for (var _i = 0, rulesDirectories_1 = rulesDirectories; _i < rulesDirectories_1.length; _i++) {
|
|
var directory = rulesDirectories_1[_i];
|
|
if (directory != null && !fs.existsSync(directory)) {
|
|
throw new Error("Could not find custom rule directory: " + directory);
|
|
}
|
|
}
|
|
return rulesDirectories;
|
|
}
|
|
exports.getRulesDirectories = getRulesDirectories;
|