@fimbul/wotan
Advanced tools
Comparing version 0.0.1 to 0.1.0-dev.20180213
{ | ||
"name": "@fimbul/wotan", | ||
"version": "0.0.1", | ||
"version": "0.1.0-dev.20180213", | ||
"description": "Pluggable TypeScript and JavaScript linter", | ||
"bin": "bin/main.js", | ||
"main": "src/index", | ||
"types": "src/index", | ||
"scripts": { | ||
"clean": "rimraf '{src,test/conformance}/**/*.{js?(.map),d.ts}'", | ||
"lint": "run-p lint:tslint lint:wotan", | ||
"lint:tslint": "tslint -p .", | ||
"lint:wotan": "node src/cli lint", | ||
"test": "run-p test:api test:rules test:integration", | ||
"clean": "rimraf '{index,{src,test/conformance}/**/*}.{js?(.map),d.ts}'", | ||
"lint": "run-p lint:*", | ||
"lint:tslint": "tslint -p tsconfig.build.json", | ||
"lint:wotan": "node src/cli lint -p tsconfig.build.json", | ||
"test": "run-p \"test:* {@}\" --", | ||
"test:api": "ava test/conformance -v --snapshot-dir baselines", | ||
"test:integration": "node src/cli test 'test/integration/**/*.test.json' --exact", | ||
"test:rules": "node src/cli test 'test/rules/**/*.test.json' --exact", | ||
"check-dependencies": "depcruise src/**/*.js bin test/conformance/*.js -v .dependency-cruiser.json" | ||
"check-dependencies": "depcruise index.js src/**/*.js bin test/conformance/*.js -v .dependency-cruiser.json" | ||
}, | ||
@@ -19,0 +17,0 @@ "publishConfig": { |
@@ -21,4 +21,5 @@ # wotan | ||
`deprecation` | Finds uses of deprecated variables, classes, properties, functions, signatures, ... *requires type information* | This rule checks element accesses (`foo[bar]`), JSX elements, chained function calls (`getFn()()`) in addition to what the TSLint rule does and has more useful error reporting. | ||
`no-debugger` | Ban `debugger;` statements from your production code. | Performance! | ||
`no-fallthrough` | Prevents unintentional fallthough in `switch` statements from one case to another. If the fallthrough is intended, add a comment that matches `/^\s*falls? ?through\b/i`. | Allows more comment variants such as `fallthrough` or `fall through`. | ||
`no-debugger` | Ban `debugger;` statements from your production code. | Performance! | ||
`no-inferred-empty-object` | Warns if a type parameter is inferred as `{}` because the compiler cannot find any inference site. | Really checks every type parameter of function, method and constructor calls. Correctly handles type parameters from JSDoc comments. Recognises type parameter defaults on all merged declarations. | ||
`no-return-await` | Warns for unnecesary `return await foo;` when you can simply `return foo;` | The same as TSLint's rule. I wrote both, but this one is faster. | ||
@@ -104,2 +105,13 @@ `no-unsafe-finally` | Forbids control flow statements `return`, `throw`, `break` and `continue` inside the `finally` block of a try statement. | Performance! | ||
## Enable or disable rules with comments | ||
Sometimes you need to enable or disable a specific rule or all rules for a section of a file. This can be done using comments. It doesn't matter if you use `//` or `/* */`. | ||
* `// wotan-disable` disables all rules from the start of the comment until the end of the file (or until it is enabled again) | ||
* `// wotan-enable` enables all rules from the start of the comment until the end of the file. Enable comments have the same mechanics as disable comments. | ||
* `// wotan-disable-line` disables all rules in the current line (also works with enable) | ||
* `// wotan-disable-next-line` disables all rules in the next line (also works with enable) | ||
* `// wotan-enable-line foo` enables the rule `foo` in the current line | ||
* `// wotan-enable-next-line bar, local/baz` enables the rules `bar` and `local/baz` in the next line | ||
## CLI Options | ||
@@ -106,0 +118,0 @@ |
@@ -10,4 +10,2 @@ "use strict"; | ||
return parseLintCommand(args.slice(1)); | ||
case "init": | ||
return parseInitCommand(args.slice(1)); | ||
case "test": | ||
@@ -166,31 +164,2 @@ return parseTestCommand(args.slice(1)); | ||
} | ||
function parseInitCommand(args) { | ||
const result = { | ||
command: "init", | ||
modules: [], | ||
directories: [], | ||
format: undefined, | ||
}; | ||
outer: for (let i = 0; i < args.length; ++i) { | ||
const arg = args[i]; | ||
switch (arg) { | ||
case '-f': | ||
case '--format': | ||
result.format = expectFormatArgument(args, ++i, arg); | ||
break; | ||
case '-m': | ||
case '--module': | ||
result.modules.push(...expectStringArgument(args, ++i, arg).split(/,/g)); | ||
break; | ||
case '--': | ||
result.directories.push(...args.slice(i + 1)); | ||
break outer; | ||
default: | ||
if (arg.startsWith('-')) | ||
throw new error_1.ConfigurationError(`Unknown option '${arg}'.`); | ||
result.directories.push(arg); | ||
} | ||
} | ||
return result; | ||
} | ||
function parseValidateCommand(_args) { | ||
@@ -197,0 +166,0 @@ throw new error_1.ConfigurationError("'validate' is not implemented yet."); |
@@ -10,3 +10,2 @@ import 'reflect-metadata'; | ||
Test = "test", | ||
Init = "init", | ||
} | ||
@@ -33,7 +32,3 @@ export interface BaseCommand<C extends CommandName> { | ||
} | ||
export interface InitCommand extends BaseCommand<CommandName.Init> { | ||
directories: string[]; | ||
format: Format | undefined; | ||
} | ||
export declare type Command = LintCommand | ShowCommand | ValidateCommand | InitCommand | TestCommand; | ||
export declare type Command = LintCommand | ShowCommand | ValidateCommand | TestCommand; | ||
export declare function runCommand(command: Command, diContainer?: Container): Promise<boolean>; |
@@ -33,5 +33,2 @@ "use strict"; | ||
break; | ||
case "init": | ||
container.bind(AbstractCommandRunner).to(InitCommandRunner); | ||
break; | ||
case "validate": | ||
@@ -119,31 +116,2 @@ container.bind(AbstractCommandRunner).to(ValidateCommandRunner); | ||
} | ||
let InitCommandRunner = class InitCommandRunner extends AbstractCommandRunner { | ||
constructor(logger, fs, directories) { | ||
super(); | ||
this.logger = logger; | ||
this.fs = fs; | ||
this.directories = directories; | ||
} | ||
run(options) { | ||
const cwd = this.directories.getCurrentDirectory(); | ||
const filename = `.wotanrc.${options.format === undefined ? 'yaml' : options.format}`; | ||
const dirs = options.directories.length === 0 ? ['.'] : options.directories; | ||
let success = true; | ||
for (const dir of dirs) { | ||
const fullPath = path.resolve(cwd, dir, filename); | ||
if (this.fs.isFile(fullPath)) { | ||
this.logger.warn(`'${fullPath}' already exists.`); | ||
success = false; | ||
} | ||
else { | ||
this.fs.writeFile(fullPath, utils_1.format({ extends: 'wotan:recommended' }, options.format)); | ||
} | ||
} | ||
return success; | ||
} | ||
}; | ||
InitCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [types_1.MessageHandler, cached_file_system_1.CachedFileSystem, types_1.DirectoryService]) | ||
], InitCommandRunner); | ||
let ValidateCommandRunner = class ValidateCommandRunner extends AbstractCommandRunner { | ||
@@ -168,6 +136,6 @@ constructor() { | ||
run(options) { | ||
const config = this.configManager.findConfiguration(options.file); | ||
const config = this.configManager.find(options.file); | ||
if (config === undefined) | ||
throw new error_1.ConfigurationError(`Could not find configuration for '${options.file}'.`); | ||
const reduced = this.configManager.reduceConfigurationForFile(config, options.file); | ||
const reduced = this.configManager.reduce(config, options.file); | ||
this.logger.log(`${config.filename}\n${reduced === undefined ? 'File is excluded.' : utils_1.format(reduced, options.format)}`); | ||
@@ -302,9 +270,11 @@ return true; | ||
function buildBaselineDirectoryName(basedir, baselineDir, testcase) { | ||
const dirname = path.dirname(testcase); | ||
let relative = path.relative(basedir, dirname); | ||
const separator = relative.indexOf(path.sep); | ||
const firstPart = relative.substr(0, separator); | ||
if (/^(__)?tests?(__)?$/.test(firstPart)) | ||
relative = relative.substr(separator + 1); | ||
return path.resolve(basedir, baselineDir, relative, getTestName(path.basename(testcase))); | ||
const parts = path.relative(basedir, path.dirname(testcase)).split(path.sep); | ||
const testIndex = parts[0] === 'packages' ? 2 : 0; | ||
if (/^(__)?tests?(__)?$/.test(parts[testIndex])) { | ||
parts[testIndex] = baselineDir; | ||
} | ||
else { | ||
parts.splice(testIndex, 0, baselineDir); | ||
} | ||
return path.resolve(basedir, parts.join(path.sep), getTestName(path.basename(testcase))); | ||
} | ||
@@ -311,0 +281,0 @@ function getTestName(basename) { |
@@ -12,2 +12,3 @@ "use strict"; | ||
const test_1 = require("../test"); | ||
const line_switches_1 = require("../services/line-switches"); | ||
exports.CORE_DI_MODULE = new inversify_1.ContainerModule((bind) => { | ||
@@ -22,4 +23,5 @@ bind(cached_file_system_1.CachedFileSystem).toSelf(); | ||
bind(test_1.RuleTester).toSelf(); | ||
bind(line_switches_1.LineSwitchService).toSelf(); | ||
bind(inversify_1.Container).toDynamicValue((context) => context.container); | ||
}); | ||
//# sourceMappingURL=core.module.js.map |
@@ -13,2 +13,4 @@ "use strict"; | ||
const deprecation_handler_1 = require("../services/default/deprecation-handler"); | ||
const configuration_provider_1 = require("../services/default/configuration-provider"); | ||
const line_switch_parser_1 = require("../services/default/line-switch-parser"); | ||
exports.DEFAULT_DI_MODULE = new inversify_1.ContainerModule((bind, _unbind, isBound) => { | ||
@@ -31,3 +33,7 @@ if (!isBound(types_1.FormatterLoaderHost)) | ||
bind(types_1.DirectoryService).to(directory_service_1.NodeDirectoryService); | ||
if (!isBound(types_1.ConfigurationProvider)) | ||
bind(types_1.ConfigurationProvider).to(configuration_provider_1.DefaultConfigurationProvider); | ||
if (!isBound(types_1.LineSwitchParser)) | ||
bind(types_1.LineSwitchParser).to(line_switch_parser_1.DefaultLineSwitchParser); | ||
}); | ||
//# sourceMappingURL=default.module.js.map |
@@ -28,3 +28,6 @@ "use strict"; | ||
this.maxNameWidth = failure.ruleName.length; | ||
const position = `${failure.start.line + 1}:${failure.start.character + 1}`; | ||
let { character, line } = failure.start; | ||
if (line !== 0 || character === 0 || !summary.content.startsWith('\uFEFF')) | ||
character += 1; | ||
const position = `${line + 1}:${character}`; | ||
if (position.length > this.maxPositionWidth) | ||
@@ -31,0 +34,0 @@ this.maxPositionWidth = position.length; |
import * as ts from 'typescript'; | ||
import { Failure, EffectiveConfiguration, LintAndFixFileResult, MessageHandler, AbstractProcessor, DeprecationHandler } from './types'; | ||
import { LineSwitchService } from './services/line-switches'; | ||
import { RuleLoader } from './services/rule-loader'; | ||
@@ -14,3 +15,4 @@ export interface UpdateFileResult { | ||
private deprecationHandler; | ||
constructor(ruleLoader: RuleLoader, logger: MessageHandler, deprecationHandler: DeprecationHandler); | ||
private lineSwitches; | ||
constructor(ruleLoader: RuleLoader, logger: MessageHandler, deprecationHandler: DeprecationHandler, lineSwitches: LineSwitchService); | ||
lintFile(file: ts.SourceFile, config: EffectiveConfiguration, program?: ts.Program): Failure[]; | ||
@@ -17,0 +19,0 @@ lintAndFix(file: ts.SourceFile, content: string, config: EffectiveConfiguration, updateFile: UpdateFileCallback, iterations?: number, program?: ts.Program, processor?: AbstractProcessor): LintAndFixFileResult; |
@@ -7,3 +7,3 @@ "use strict"; | ||
const fix_1 = require("./fix"); | ||
const line_switches_1 = require("./line-switches"); | ||
const line_switches_1 = require("./services/line-switches"); | ||
const debug = require("debug"); | ||
@@ -16,6 +16,7 @@ const inversify_1 = require("inversify"); | ||
let Linter = class Linter { | ||
constructor(ruleLoader, logger, deprecationHandler) { | ||
constructor(ruleLoader, logger, deprecationHandler, lineSwitches) { | ||
this.ruleLoader = ruleLoader; | ||
this.logger = logger; | ||
this.deprecationHandler = deprecationHandler; | ||
this.lineSwitches = lineSwitches; | ||
} | ||
@@ -100,2 +101,7 @@ lintFile(file, config, program) { | ||
let convertedAst; | ||
const isDisabled = (range) => { | ||
if (disables === undefined) | ||
disables = this.lineSwitches.getDisabledRanges(sourceFile, rules.map((r) => r.ruleName), getWrappedAst); | ||
return this.lineSwitches.isDisabled(disables, ruleName, range); | ||
}; | ||
const context = { | ||
@@ -135,13 +141,2 @@ addFailure, | ||
} | ||
function isDisabled(range) { | ||
if (disables === undefined) | ||
disables = line_switches_1.getDisabledRanges(rules.map((r) => r.ruleName), sourceFile, getWrappedAst()); | ||
const ruleDisables = disables.get(ruleName); | ||
if (ruleDisables === undefined) | ||
return false; | ||
for (const disabledRange of ruleDisables) | ||
if (range.end > disabledRange.pos && range.pos < disabledRange.end) | ||
return true; | ||
return false; | ||
} | ||
function getFlatAst() { | ||
@@ -157,5 +152,8 @@ return (convertedAst || (convertedAst = tsutils_1.convertAst(sourceFile))).flat; | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [rule_loader_1.RuleLoader, types_1.MessageHandler, types_1.DeprecationHandler]) | ||
tslib_1.__metadata("design:paramtypes", [rule_loader_1.RuleLoader, | ||
types_1.MessageHandler, | ||
types_1.DeprecationHandler, | ||
line_switches_1.LineSwitchService]) | ||
], Linter); | ||
exports.Linter = Linter; | ||
//# sourceMappingURL=linter.js.map |
@@ -49,8 +49,8 @@ "use strict"; | ||
if (c === 'initial') | ||
c = this.configManager.findConfiguration(fileName); | ||
const processor = c && this.configManager.getProcessorForFile(c, fileName); | ||
c = this.configManager.find(fileName); | ||
const processor = c && this.configManager.getProcessor(c, fileName); | ||
if (processor) { | ||
const ctor = this.processorLoader.loadProcessor(processor); | ||
const newName = fileName + | ||
ctor.getSuffixForFile(fileName, this.configManager.getSettingsForFile(c, fileName), () => this.fs.readFile(fileName)); | ||
ctor.getSuffixForFile(fileName, this.configManager.getSettings(c, fileName), () => this.fs.readFile(fileName)); | ||
if (utils_1.hasSupportedExtension(newName)) { | ||
@@ -107,10 +107,10 @@ files.push(newName); | ||
let content = this.fs.readFile(realFile); | ||
const config = this.config || this.configManager.findConfiguration(realFile); | ||
const config = this.config || this.configManager.find(realFile); | ||
if (config === undefined) | ||
return content; | ||
const processorPath = this.configManager.getProcessorForFile(config, realFile); | ||
const processorPath = this.configManager.getProcessor(config, realFile); | ||
if (processorPath === undefined) | ||
return content; | ||
const ctor = this.processorLoader.loadProcessor(processorPath); | ||
const processor = new ctor(content, realFile, file, this.configManager.getSettingsForFile(config, realFile)); | ||
const processor = new ctor(content, realFile, file, this.configManager.getSettings(config, realFile)); | ||
this.processedFiles.set(file, { | ||
@@ -117,0 +117,0 @@ processor, |
@@ -12,2 +12,6 @@ "use strict"; | ||
const typescriptPre270 = /^2\.[456]\./.test(ts.version); | ||
const typeFormat = ts.TypeFormatFlags.NoTruncation | ||
| ts.TypeFormatFlags.UseFullyQualifiedType | ||
| ts.TypeFormatFlags.WriteClassExpressionAsTypeLiteral | ||
| ts.TypeFormatFlags.UseStructuralFallback; | ||
class Rule extends types_1.TypedRule { | ||
@@ -95,3 +99,3 @@ constructor() { | ||
const start = node.getStart(this.sourceFile); | ||
this.addFailure(start, node.expression.pos, FAIL_MESSAGE, types_1.Replacement.delete(start, node.expression.getStart(this.sourceFile))); | ||
this.addFailure(start, node.expression.pos, message, types_1.Replacement.delete(start, node.expression.getStart(this.sourceFile))); | ||
} | ||
@@ -108,3 +112,3 @@ } | ||
function typesAreEqual(a, b, checker) { | ||
return a === b || checker.typeToString(a) === checker.typeToString(b); | ||
return a === b || checker.typeToString(a, undefined, typeFormat) === checker.typeToString(b, undefined, typeFormat); | ||
} | ||
@@ -111,0 +115,0 @@ function getNullableFlags(type, receiver) { |
@@ -35,14 +35,8 @@ "use strict"; | ||
let { files, program } = this.getFilesAndProgram(options.project, options.files, options.exclude, processorHost); | ||
let dir; | ||
for (const file of files) { | ||
if (options.config === undefined) { | ||
const dirname = path.dirname(file); | ||
if (dir !== dirname) { | ||
config = this.configManager.findConfiguration(file); | ||
dir = dirname; | ||
} | ||
} | ||
if (options.config === undefined) | ||
config = this.configManager.find(file); | ||
const mapped = processorHost.getProcessedFileInfo(file); | ||
const originalName = mapped === undefined ? file : mapped.originalName; | ||
const effectiveConfig = config && this.configManager.reduceConfigurationForFile(config, originalName); | ||
const effectiveConfig = config && this.configManager.reduce(config, originalName); | ||
if (effectiveConfig === undefined) | ||
@@ -70,13 +64,7 @@ continue; | ||
*lintFiles(options, config) { | ||
let dir; | ||
let processor; | ||
for (const file of getFiles(options.files, options.exclude, this.directories.getCurrentDirectory())) { | ||
if (options.config === undefined) { | ||
const dirname = path.dirname(file); | ||
if (dir !== dirname) { | ||
config = this.configManager.findConfiguration(file); | ||
dir = dirname; | ||
} | ||
} | ||
const effectiveConfig = config && this.configManager.reduceConfigurationForFile(config, file); | ||
if (options.config === undefined) | ||
config = this.configManager.find(file); | ||
const effectiveConfig = config && this.configManager.reduce(config, file); | ||
if (effectiveConfig === undefined) | ||
@@ -132,4 +120,4 @@ continue; | ||
? absolute | ||
: this.configManager.resolveConfigFile(pathOrName, this.directories.getCurrentDirectory()); | ||
return this.configManager.loadConfigurationFromPath(resolved); | ||
: this.configManager.resolve(pathOrName, this.directories.getCurrentDirectory()); | ||
return this.configManager.load(resolved); | ||
} | ||
@@ -223,3 +211,3 @@ getFilesAndProgram(project, patterns, exclude, host) { | ||
} | ||
return Array.from(new Set(result.map(utils_1.unixifyPath))); | ||
return new Set(result.map(utils_1.unixifyPath)); | ||
} | ||
@@ -226,0 +214,0 @@ function ensurePatternsMatch(include, exclude, files) { |
@@ -1,27 +0,14 @@ | ||
import { CachedFileSystem } from './cached-file-system'; | ||
import { Resolver, Configuration, RawConfiguration, CacheManager, ReducedConfiguration, GlobalSettings, DirectoryService } from '../types'; | ||
export declare const CONFIG_EXTENSIONS: string[]; | ||
export declare const CONFIG_FILENAMES: string[]; | ||
import { Configuration, CacheManager, ReducedConfiguration, GlobalSettings, DirectoryService, ConfigurationProvider } from '../types'; | ||
export declare class ConfigurationManager { | ||
private fs; | ||
private resolver; | ||
private directories; | ||
private configProvider; | ||
private configCache; | ||
constructor(fs: CachedFileSystem, resolver: Resolver, directories: DirectoryService, cache: CacheManager); | ||
findConfigurationPath(file: string): string | undefined; | ||
findConfiguration(file: string): Configuration | undefined; | ||
readConfigurationFile(filename: string): RawConfiguration; | ||
resolveConfigFile(name: string, basedir: string): string; | ||
reduceConfigurationForFile(config: Configuration, file: string): ReducedConfiguration | undefined; | ||
getProcessorForFile(config: Configuration, file: string): string | undefined; | ||
getSettingsForFile(config: Configuration, file: string): GlobalSettings; | ||
loadConfigurationFromPath(file: string): Configuration; | ||
private loadConfigurationFromPathWorker(file, stack); | ||
parseConfiguration(raw: RawConfiguration, filename: string): Configuration; | ||
private parseConfigWorker(raw, filename, stack); | ||
private mapOverride(raw, basedir); | ||
private mapProcessor(processor, basedir); | ||
private mapRulesDirectory(raw, dirname); | ||
private findupConfig(current); | ||
private findConfigFileInDirectory(dir); | ||
constructor(directories: DirectoryService, configProvider: ConfigurationProvider, cache: CacheManager); | ||
findPath(file: string): string | undefined; | ||
find(file: string): Configuration | undefined; | ||
resolve(name: string, basedir: string): string; | ||
reduce(config: Configuration, file: string): ReducedConfiguration | undefined; | ||
getProcessor(config: Configuration, fileName: string): string | undefined; | ||
getSettings(config: Configuration, fileName: string): GlobalSettings; | ||
load(fileName: string): Configuration; | ||
} |
@@ -5,145 +5,65 @@ "use strict"; | ||
const inversify_1 = require("inversify"); | ||
const cached_file_system_1 = require("./cached-file-system"); | ||
const types_1 = require("../types"); | ||
const path = require("path"); | ||
const json5 = require("json5"); | ||
const error_1 = require("../error"); | ||
const yaml = require("js-yaml"); | ||
const utils_1 = require("../utils"); | ||
const configuration_1 = require("../configuration"); | ||
exports.CONFIG_EXTENSIONS = ['.yaml', '.yml', '.json5', '.json', '.js']; | ||
exports.CONFIG_FILENAMES = exports.CONFIG_EXTENSIONS.map((ext) => '.wotanrc' + ext); | ||
const minimatch_1 = require("minimatch"); | ||
const isNegated = require("is-negated-glob"); | ||
const configCache = new types_1.CacheIdentifier('configuration'); | ||
let ConfigurationManager = class ConfigurationManager { | ||
constructor(fs, resolver, directories, cache) { | ||
this.fs = fs; | ||
this.resolver = resolver; | ||
constructor(directories, configProvider, cache) { | ||
this.directories = directories; | ||
this.configProvider = configProvider; | ||
this.configCache = cache.create(configCache); | ||
} | ||
findConfigurationPath(file) { | ||
let result = this.findupConfig(path.resolve(this.directories.getCurrentDirectory(), file)); | ||
if (result === undefined && this.directories.getHomeDirectory !== undefined) | ||
result = this.findConfigFileInDirectory(this.directories.getHomeDirectory()); | ||
return result; | ||
} | ||
findConfiguration(file) { | ||
const config = this.findConfigurationPath(file); | ||
return config === undefined ? undefined : this.loadConfigurationFromPath(config); | ||
} | ||
readConfigurationFile(filename) { | ||
filename = path.resolve(this.directories.getCurrentDirectory(), filename); | ||
findPath(file) { | ||
file = path.resolve(this.directories.getCurrentDirectory(), file); | ||
try { | ||
switch (path.extname(filename)) { | ||
case '.json': | ||
case '.json5': | ||
return json5.parse(this.fs.readFile(filename)); | ||
case '.yaml': | ||
case '.yml': | ||
return yaml.safeLoad(this.fs.readFile(filename), { | ||
schema: yaml.JSON_SCHEMA, | ||
strict: true, | ||
}); | ||
default: | ||
return this.resolver.require(filename, { cache: false }); | ||
} | ||
return this.configProvider.find(file); | ||
} | ||
catch (e) { | ||
throw new error_1.ConfigurationError(`Error parsing '${filename}': ${e && e.message}`); | ||
throw new error_1.ConfigurationError(`Error finding configuration for '${file}': ${e && e.message}`); | ||
} | ||
} | ||
resolveConfigFile(name, basedir) { | ||
if (name.startsWith('wotan:')) { | ||
const fileName = path.join(__dirname, `../configs/${name.substr('wotan:'.length)}.yaml`); | ||
if (!this.fs.isFile(fileName)) | ||
throw new error_1.ConfigurationError(`'${name}' is not a valid builtin configuration, try 'wotan:recommended'.`); | ||
return fileName; | ||
} | ||
basedir = path.resolve(this.directories.getCurrentDirectory(), basedir); | ||
find(file) { | ||
const config = this.findPath(file); | ||
return config === undefined ? undefined : this.load(config); | ||
} | ||
resolve(name, basedir) { | ||
try { | ||
return this.resolver.resolve(name, basedir, exports.CONFIG_EXTENSIONS, module.paths.slice(utils_1.OFFSET_TO_NODE_MODULES + 1)); | ||
return this.configProvider.resolve(name, path.resolve(this.directories.getCurrentDirectory(), basedir)); | ||
} | ||
catch (e) { | ||
throw new error_1.ConfigurationError(e.message); | ||
throw new error_1.ConfigurationError(`${e && e.message}`); | ||
} | ||
} | ||
reduceConfigurationForFile(config, file) { | ||
return configuration_1.reduceConfigurationForFile(config, file, this.directories.getCurrentDirectory()); | ||
reduce(config, file) { | ||
return reduceConfig(config, path.resolve(this.directories.getCurrentDirectory(), file), { rules: new Map(), settings: new Map(), processor: undefined }); | ||
} | ||
getProcessorForFile(config, file) { | ||
return configuration_1.getProcessorForFile(config, file, this.directories.getCurrentDirectory()); | ||
getProcessor(config, fileName) { | ||
return findProcessorInConfig(config, path.resolve(this.directories.getCurrentDirectory(), fileName)) || undefined; | ||
} | ||
getSettingsForFile(config, file) { | ||
return configuration_1.getSettingsForFile(config, file, this.directories.getCurrentDirectory()); | ||
getSettings(config, fileName) { | ||
return reduceSettings(config, path.resolve(this.directories.getCurrentDirectory(), fileName), new Map()); | ||
} | ||
loadConfigurationFromPath(file) { | ||
return this.loadConfigurationFromPathWorker(path.resolve(this.directories.getCurrentDirectory(), file), [file]); | ||
} | ||
loadConfigurationFromPathWorker(file, stack) { | ||
return utils_1.resolveCachedResult(this.configCache, file, () => { | ||
return this.parseConfigWorker(this.readConfigurationFile(file), file, stack); | ||
}); | ||
} | ||
parseConfiguration(raw, filename) { | ||
filename = path.resolve(this.directories.getCurrentDirectory(), filename); | ||
return this.parseConfigWorker(raw, filename, [filename]); | ||
} | ||
parseConfigWorker(raw, filename, stack) { | ||
const dirname = path.dirname(filename); | ||
const base = utils_1.arrayify(raw.extends).map((name) => { | ||
name = this.resolveConfigFile(name, dirname); | ||
if (stack.includes(name)) | ||
throw new error_1.ConfigurationError(`Circular configuration dependency ${stack.join(' => ')} => ${name}`); | ||
return this.loadConfigurationFromPathWorker(name, [...stack, name]); | ||
}); | ||
return { | ||
filename, | ||
extends: base, | ||
aliases: raw.aliases && mapAliases(raw.aliases), | ||
overrides: raw.overrides && raw.overrides.map((o) => this.mapOverride(o, dirname)), | ||
rules: raw.rules && mapRules(raw.rules), | ||
rulesDirectories: raw.rulesDirectories && this.mapRulesDirectory(raw.rulesDirectories, dirname), | ||
processor: this.mapProcessor(raw.processor, dirname), | ||
exclude: Array.isArray(raw.exclude) ? raw.exclude : raw.exclude === undefined ? undefined : [raw.exclude], | ||
settings: raw.settings && mapSettings(raw.settings), | ||
load(fileName) { | ||
const stack = []; | ||
const loadResolved = (file) => { | ||
const circular = stack.includes(file); | ||
stack.push(file); | ||
if (circular) | ||
throw new Error(`Circular configuration dependency.`); | ||
const config = this.configProvider.load(file, { | ||
stack, | ||
load: (name) => utils_1.resolveCachedResult(this.configCache, this.configProvider.resolve(name, path.dirname(file)), loadResolved), | ||
}); | ||
stack.pop(); | ||
return config; | ||
}; | ||
} | ||
mapOverride(raw, basedir) { | ||
const files = utils_1.arrayify(raw.files); | ||
if (files.length === 0) | ||
throw new error_1.ConfigurationError(`Override does not specify files.`); | ||
return { | ||
files: utils_1.arrayify(raw.files), | ||
rules: raw.rules && mapRules(raw.rules), | ||
settings: raw.settings && mapSettings(raw.settings), | ||
processor: this.mapProcessor(raw.processor, basedir), | ||
}; | ||
} | ||
mapProcessor(processor, basedir) { | ||
return processor && this.resolver.resolve(processor, basedir, Object.keys(require.extensions).filter((ext) => ext !== '.json' && ext !== '.node'), module.paths.slice(utils_1.OFFSET_TO_NODE_MODULES + 1)); | ||
} | ||
mapRulesDirectory(raw, dirname) { | ||
const result = new Map(); | ||
for (const key of Object.keys(raw)) | ||
result.set(key, path.resolve(dirname, raw[key])); | ||
return result; | ||
} | ||
findupConfig(current) { | ||
let next = path.dirname(current); | ||
while (next !== current) { | ||
current = next; | ||
const config = this.findConfigFileInDirectory(current); | ||
if (config !== undefined) | ||
return config; | ||
next = path.dirname(next); | ||
try { | ||
return utils_1.resolveCachedResult(this.configCache, path.resolve(this.directories.getCurrentDirectory(), fileName), loadResolved); | ||
} | ||
return; | ||
} | ||
findConfigFileInDirectory(dir) { | ||
for (let name of exports.CONFIG_FILENAMES) { | ||
name = path.join(dir, name); | ||
if (this.fs.isFile(name)) | ||
return name; | ||
catch (e) { | ||
throw new error_1.ConfigurationError(`Error loading ${stack.join(' => ')}: ${e && e.message}`); | ||
} | ||
return; | ||
} | ||
@@ -153,59 +73,81 @@ }; | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [cached_file_system_1.CachedFileSystem, | ||
types_1.Resolver, | ||
types_1.DirectoryService, | ||
tslib_1.__metadata("design:paramtypes", [types_1.DirectoryService, | ||
types_1.ConfigurationProvider, | ||
types_1.CacheManager]) | ||
], ConfigurationManager); | ||
exports.ConfigurationManager = ConfigurationManager; | ||
function mapAliases(aliases) { | ||
const result = new Map(); | ||
for (const prefix of Object.keys(aliases)) { | ||
const obj = aliases[prefix]; | ||
if (!obj) | ||
continue; | ||
for (const name of Object.keys(obj)) { | ||
const config = obj[name]; | ||
const fullName = `${prefix}/${name}`; | ||
if (config && !config.rule) | ||
throw new error_1.ConfigurationError(`Alias '${fullName}' does not specify a rule.`); | ||
result.set(fullName, config); | ||
} | ||
function reduceConfig(config, filename, receiver) { | ||
const relativeFilename = path.relative(path.dirname(config.filename), filename); | ||
if (config.exclude !== undefined && matchesGlobs(relativeFilename, config.exclude)) | ||
return; | ||
for (const base of config.extends) | ||
if (reduceConfig(base, filename, receiver) === undefined) | ||
return; | ||
extendConfig(receiver, config); | ||
if (config.overrides !== undefined) | ||
for (const override of config.overrides) | ||
if (matchesGlobs(relativeFilename, override.files)) | ||
extendConfig(receiver, override); | ||
return receiver; | ||
} | ||
function extendConfig(receiver, config) { | ||
if (config.processor !== undefined) | ||
receiver.processor = config.processor || undefined; | ||
if (config.settings !== undefined) | ||
extendSettings(receiver.settings, config.settings); | ||
if (config.rules !== undefined) | ||
extendRules(receiver.rules, config.rules); | ||
} | ||
function matchesGlobs(file, patterns) { | ||
for (let i = patterns.length - 1; i >= 0; --i) { | ||
const glob = isNegated(patterns[i]); | ||
const local = glob.pattern.startsWith('./'); | ||
if (local) | ||
glob.pattern = glob.pattern.substr(2); | ||
if (new minimatch_1.Minimatch(glob.pattern, { matchBase: !local }).match(file)) | ||
return !glob.negated; | ||
} | ||
return result; | ||
return false; | ||
} | ||
function mapRules(raw) { | ||
const result = new Map(); | ||
for (const key of Object.keys(raw)) | ||
result.set(key, mapRuleConfig(raw[key])); | ||
return result; | ||
function extendRules(receiver, rules) { | ||
for (const [ruleName, config] of rules) { | ||
const prev = receiver.get(ruleName); | ||
receiver.set(ruleName, Object.assign({ severity: 'error', options: undefined }, prev, config)); | ||
} | ||
} | ||
function mapRuleConfig(value) { | ||
if (typeof value === 'string') | ||
return { severity: mapRuleSeverity(value) }; | ||
if (!value) | ||
return {}; | ||
const result = {}; | ||
if ('options' in value) | ||
result.options = value.options; | ||
if ('severity' in value) | ||
result.severity = mapRuleSeverity(value.severity); | ||
return result; | ||
function extendSettings(receiver, settings) { | ||
for (const [key, value] of settings) | ||
receiver.set(key, value); | ||
} | ||
function mapRuleSeverity(severity) { | ||
switch (severity) { | ||
case 'off': | ||
return 'off'; | ||
case 'warn': | ||
case 'warning': | ||
return 'warning'; | ||
default: | ||
return 'error'; | ||
function findProcessorInConfig(config, fileName) { | ||
if (config.overrides !== undefined) { | ||
const relative = path.relative(path.dirname(config.filename), fileName); | ||
for (let i = config.overrides.length - 1; i >= 0; --i) { | ||
const override = config.overrides[i]; | ||
if (override.processor !== undefined && matchesGlobs(relative, override.files)) | ||
return override.processor; | ||
} | ||
} | ||
if (config.processor !== undefined) | ||
return config.processor; | ||
for (let i = config.extends.length - 1; i >= 0; --i) { | ||
const processor = findProcessorInConfig(config.extends[i], fileName); | ||
if (processor !== undefined) | ||
return processor; | ||
} | ||
return; | ||
} | ||
function mapSettings(settings) { | ||
const result = new Map(); | ||
for (const key of Object.keys(settings)) | ||
result.set(key, settings[key]); | ||
return result; | ||
function reduceSettings(config, fileName, receiver) { | ||
for (const base of config.extends) | ||
reduceSettings(base, fileName, receiver); | ||
if (config.settings !== undefined) | ||
extendSettings(receiver, config.settings); | ||
if (config.overrides !== undefined) { | ||
const relative = path.relative(path.dirname(config.filename), fileName); | ||
for (const override of config.overrides) | ||
if (override.settings && matchesGlobs(relative, override.files)) | ||
extendSettings(receiver, override.settings); | ||
} | ||
return receiver; | ||
} | ||
//# sourceMappingURL=configuration-manager.js.map |
@@ -1,3 +0,5 @@ | ||
import { FileSystem, Stats } from '../../types'; | ||
import { FileSystem, Stats, MessageHandler } from '../../types'; | ||
export declare class NodeFileSystem implements FileSystem { | ||
private logger; | ||
constructor(logger: MessageHandler); | ||
normalizePath(path: string): string; | ||
@@ -4,0 +6,0 @@ readFile(file: string): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tslib_1 = require("tslib"); | ||
const types_1 = require("../../types"); | ||
const fs = require("fs"); | ||
@@ -9,2 +10,5 @@ const inversify_1 = require("inversify"); | ||
let NodeFileSystem = class NodeFileSystem { | ||
constructor(logger) { | ||
this.logger = logger; | ||
} | ||
normalizePath(path) { | ||
@@ -16,2 +20,9 @@ return utils_1.unixifyPath(ts.sys.useCaseSensitiveFileNames ? path : path.toLowerCase()); | ||
const len = buf.length; | ||
outer: while (len > 188 && buf[0] === 0x47) { | ||
for (let i = 188; i < len; i += 188) | ||
if (buf[i] !== 0x47) | ||
break outer; | ||
this.logger.warn(`Detected MPEG TS file: '${file}'.`); | ||
return ''; | ||
} | ||
if (len >= 2) { | ||
@@ -47,5 +58,6 @@ if (buf[0] === 0xFE && buf[1] === 0xFF) | ||
NodeFileSystem = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [types_1.MessageHandler]) | ||
], NodeFileSystem); | ||
exports.NodeFileSystem = NodeFileSystem; | ||
//# sourceMappingURL=file-system.js.map |
@@ -7,5 +7,5 @@ import { RuleLoaderHost, RuleConstructor, MessageHandler, CacheManager } from '../types'; | ||
constructor(host: RuleLoaderHost, logger: MessageHandler, cache: CacheManager); | ||
loadRule(name: string, directories: string[] | undefined): RuleConstructor | undefined; | ||
loadRule(name: string, directories: ReadonlyArray<string> | undefined): RuleConstructor | undefined; | ||
private loadCoreRule(name); | ||
private loadCustomRule(cacheKey); | ||
} |
@@ -6,3 +6,2 @@ "use strict"; | ||
const types_1 = require("../types"); | ||
const error_1 = require("../error"); | ||
const debug = require("debug"); | ||
@@ -20,4 +19,3 @@ const bind_decorator_1 = require("bind-decorator"); | ||
loadRule(name, directories) { | ||
const slashIndex = name.lastIndexOf('/'); | ||
if (slashIndex === -1) { | ||
if (directories === undefined) { | ||
const ctor = utils_1.resolveCachedResult(this.cache, name, this.loadCoreRule); | ||
@@ -28,5 +26,2 @@ if (ctor === undefined) | ||
} | ||
if (directories === undefined) | ||
throw new error_1.ConfigurationError(`No 'rulesDirectories' for rule '${name}'.`); | ||
name = name.substr(slashIndex + 1); | ||
for (const dir of directories) { | ||
@@ -33,0 +28,0 @@ const ctor = utils_1.resolveCachedResult(this.cache, `${dir}💩${name}`, this.loadCustomRule); |
@@ -45,4 +45,4 @@ import * as ts from 'typescript'; | ||
readonly options: {} | null | undefined; | ||
addFailure(this: void, start: number, end: number, message: string, fix?: Replacement | Replacement[]): void; | ||
isDisabled(this: void, range: ts.TextRange): boolean; | ||
addFailure(start: number, end: number, message: string, fix?: Replacement | Replacement[]): void; | ||
isDisabled(range: ts.TextRange): boolean; | ||
getFlatAst(): ReadonlyArray<ts.Node>; | ||
@@ -95,70 +95,32 @@ getWrappedAst(): WrappedAst; | ||
} | ||
export interface RawConfiguration { | ||
aliases?: { | ||
[prefix: string]: { | ||
[name: string]: RawConfiguration.Alias | null | false; | ||
}; | ||
}; | ||
rules?: { | ||
[key: string]: RawConfiguration.RuleConfigValue; | ||
}; | ||
settings?: { | ||
[key: string]: any; | ||
}; | ||
extends?: string | string[]; | ||
overrides?: RawConfiguration.Override[]; | ||
rulesDirectories?: { | ||
[prefix: string]: string; | ||
}; | ||
exclude?: string | string[]; | ||
processor?: string | null | false; | ||
} | ||
export declare namespace RawConfiguration { | ||
type RuleSeverity = 'off' | 'warn' | 'warning' | 'error'; | ||
interface RuleConfig { | ||
severity?: RuleSeverity; | ||
options?: any; | ||
} | ||
type RuleConfigValue = RuleSeverity | RuleConfig | null; | ||
interface Override { | ||
files: string | string[]; | ||
rules?: { | ||
[key: string]: RawConfiguration.RuleConfigValue; | ||
}; | ||
settings?: { | ||
[key: string]: any; | ||
}; | ||
processor?: string | null | false; | ||
} | ||
interface Alias { | ||
rule: string; | ||
options?: any; | ||
} | ||
} | ||
export interface Configuration { | ||
aliases?: Map<string, Configuration.Alias | null | false>; | ||
rules?: Map<string, Configuration.RuleConfig>; | ||
settings?: Map<string, any>; | ||
filename: string; | ||
overrides?: Configuration.Override[]; | ||
extends: Configuration[]; | ||
rulesDirectories?: Map<string, string>; | ||
processor?: string | null | false; | ||
exclude?: string[]; | ||
readonly aliases?: ReadonlyMap<string, Configuration.Alias>; | ||
readonly rules?: ReadonlyMap<string, Configuration.RuleConfig>; | ||
readonly settings?: GlobalSettings; | ||
readonly filename: string; | ||
readonly overrides?: ReadonlyArray<Configuration.Override>; | ||
readonly extends: ReadonlyArray<Configuration>; | ||
readonly rulesDirectories?: Configuration.RulesDirectoryMap; | ||
readonly processor?: string | null | false; | ||
readonly exclude?: ReadonlyArray<string>; | ||
} | ||
export declare namespace Configuration { | ||
type RulesDirectoryMap = ReadonlyMap<string, ReadonlyArray<string>>; | ||
type RuleSeverity = 'off' | 'warning' | 'error'; | ||
interface RuleConfig { | ||
severity?: RuleSeverity; | ||
options?: any; | ||
readonly severity?: RuleSeverity; | ||
readonly options?: any; | ||
readonly rulesDirectories: ReadonlyArray<string> | undefined; | ||
readonly rule: string; | ||
} | ||
interface Override { | ||
rules?: Map<string, RuleConfig>; | ||
settings?: Map<string, any>; | ||
files: string[]; | ||
processor?: string | null | false; | ||
readonly rules?: ReadonlyMap<string, RuleConfig>; | ||
readonly settings?: ReadonlyMap<string, any>; | ||
readonly files: ReadonlyArray<string>; | ||
readonly processor?: string | null | false; | ||
} | ||
interface Alias { | ||
rule: string; | ||
options?: any; | ||
readonly rule: string; | ||
readonly options?: any; | ||
readonly rulesDirectories: ReadonlyArray<string> | undefined; | ||
} | ||
@@ -174,3 +136,3 @@ } | ||
options: any; | ||
rulesDirectories: string[] | undefined; | ||
rulesDirectories: ReadonlyArray<string> | undefined; | ||
rule: string; | ||
@@ -182,2 +144,13 @@ } | ||
} | ||
export interface ConfigurationProvider { | ||
find(fileToLint: string): string | undefined; | ||
resolve(name: string, basedir: string): string; | ||
load(fileName: string, context: LoadConfigurationContext): Configuration; | ||
} | ||
export declare abstract class ConfigurationProvider { | ||
} | ||
export interface LoadConfigurationContext { | ||
stack: ReadonlyArray<string>; | ||
load(name: string): Configuration; | ||
} | ||
export declare const enum Format { | ||
@@ -285,1 +258,13 @@ Yaml = "yaml", | ||
} | ||
export interface LineSwitchParser { | ||
parse(sourceFile: ts.SourceFile, ruleNames: ReadonlyArray<string>, context: LineSwitchParserContext): ReadonlyMap<string, ReadonlyArray<LineSwitch>>; | ||
} | ||
export declare abstract class LineSwitchParser { | ||
} | ||
export interface LineSwitchParserContext { | ||
getCommentAtPosition(pos: number): ts.CommentRange | undefined; | ||
} | ||
export interface LineSwitch { | ||
readonly enable: boolean; | ||
readonly position: number; | ||
} |
@@ -87,2 +87,5 @@ "use strict"; | ||
exports.AbstractFormatter = AbstractFormatter; | ||
class ConfigurationProvider { | ||
} | ||
exports.ConfigurationProvider = ConfigurationProvider; | ||
class AbstractProcessor { | ||
@@ -138,2 +141,5 @@ constructor(source, sourceFileName, targetFileName, settings) { | ||
exports.DirectoryService = DirectoryService; | ||
class LineSwitchParser { | ||
} | ||
exports.LineSwitchParser = LineSwitchParser; | ||
//# sourceMappingURL=types.js.map |
@@ -6,3 +6,3 @@ "use strict"; | ||
const path = require("path"); | ||
exports.OFFSET_TO_NODE_MODULES = 2; | ||
exports.OFFSET_TO_NODE_MODULES = 3; | ||
function isStrictNullChecksEnabled(options) { | ||
@@ -9,0 +9,0 @@ return options.strict ? options.strictNullChecks !== false : options.strictNullChecks === true; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
353849
139
4505
142
1