@fimbul/wotan
Advanced tools
Comparing version
@@ -23,4 +23,4 @@ "use strict"; | ||
var argparse_1 = require("./src/argparse"); | ||
exports.parseGlobalOptions = argparse_1.parseGlobalOptions; | ||
exports.GLOBAL_OPTIONS_SPEC = argparse_1.GLOBAL_OPTIONS_SPEC; | ||
Object.defineProperty(exports, "parseGlobalOptions", { enumerable: true, get: function () { return argparse_1.parseGlobalOptions; } }); | ||
Object.defineProperty(exports, "GLOBAL_OPTIONS_SPEC", { enumerable: true, get: function () { return argparse_1.GLOBAL_OPTIONS_SPEC; } }); | ||
tslib_1.__exportStar(require("./src/baseline"), exports); | ||
@@ -27,0 +27,0 @@ tslib_1.__exportStar(require("./src/di/core.module"), exports); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.LanguageServiceInterceptor = exports.version = void 0; | ||
const ts = require("typescript"); | ||
@@ -4,0 +5,0 @@ const ymir_1 = require("@fimbul/ymir"); |
{ | ||
"name": "@fimbul/wotan", | ||
"version": "0.22.0-dev.20200204", | ||
"version": "0.22.0-dev.20201207", | ||
"description": "Pluggable TypeScript and JavaScript linter", | ||
@@ -5,0 +5,0 @@ "bin": "bin/main.js", |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseGlobalOptions = exports.GLOBAL_OPTIONS_SPEC = exports.parseArguments = void 0; | ||
const ymir_1 = require("@fimbul/ymir"); | ||
@@ -4,0 +5,0 @@ const debug = require("debug"); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createBaseline = exports.isCodeLine = void 0; | ||
const ymir_1 = require("@fimbul/ymir"); | ||
@@ -4,0 +5,0 @@ function isCodeLine(line) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.loadConfig = exports.run = void 0; | ||
const ymir_1 = require("@fimbul/ymir"); | ||
@@ -4,0 +5,0 @@ const fs = require("fs"); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.AbstractCommandRunner = exports.CommandName = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -13,8 +14,11 @@ const inversify_1 = require("inversify"); | ||
})(CommandName = exports.CommandName || (exports.CommandName = {})); | ||
let AbstractCommandRunner = class AbstractCommandRunner { | ||
}; | ||
AbstractCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], AbstractCommandRunner); | ||
let AbstractCommandRunner = /** @class */ (() => { | ||
let AbstractCommandRunner = class AbstractCommandRunner { | ||
}; | ||
AbstractCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], AbstractCommandRunner); | ||
return AbstractCommandRunner; | ||
})(); | ||
exports.AbstractCommandRunner = AbstractCommandRunner; | ||
//# sourceMappingURL=base.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.runCommand = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -4,0 +5,0 @@ require("reflect-metadata"); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.module = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -10,40 +11,43 @@ const base_1 = require("./base"); | ||
const cached_file_system_1 = require("../services/cached-file-system"); | ||
let LintCommandRunner = class LintCommandRunner extends base_1.AbstractCommandRunner { | ||
constructor(runner, formatterLoader, logger, fs) { | ||
super(); | ||
this.runner = runner; | ||
this.formatterLoader = formatterLoader; | ||
this.logger = logger; | ||
this.fs = fs; | ||
} | ||
run(options) { | ||
const formatter = new (this.formatterLoader.loadFormatter(options.formatter === undefined ? 'stylish' : options.formatter))(); | ||
const result = this.runner.lintCollection(options); | ||
let success = true; | ||
if (formatter.prefix !== undefined) | ||
this.logger.log(formatter.prefix); | ||
for (const [file, summary] of result) { | ||
if (summary.findings.some((f) => f.severity === 'error')) | ||
success = false; | ||
const formatted = formatter.format(file, summary); | ||
if (formatted !== undefined) | ||
this.logger.log(formatted); | ||
if (options.fix && summary.fixes) | ||
this.fs.writeFile(file, summary.content); | ||
let LintCommandRunner = /** @class */ (() => { | ||
let LintCommandRunner = class LintCommandRunner extends base_1.AbstractCommandRunner { | ||
constructor(runner, formatterLoader, logger, fs) { | ||
super(); | ||
this.runner = runner; | ||
this.formatterLoader = formatterLoader; | ||
this.logger = logger; | ||
this.fs = fs; | ||
} | ||
if (formatter.flush !== undefined) { | ||
const formatted = formatter.flush(); | ||
if (formatted !== undefined) | ||
this.logger.log(formatted); | ||
run(options) { | ||
const formatter = new (this.formatterLoader.loadFormatter(options.formatter === undefined ? 'stylish' : options.formatter))(); | ||
const result = this.runner.lintCollection(options); | ||
let success = true; | ||
if (formatter.prefix !== undefined) | ||
this.logger.log(formatter.prefix); | ||
for (const [file, summary] of result) { | ||
if (summary.findings.some((f) => f.severity === 'error')) | ||
success = false; | ||
const formatted = formatter.format(file, summary); | ||
if (formatted !== undefined) | ||
this.logger.log(formatted); | ||
if (options.fix && summary.fixes) | ||
this.fs.writeFile(file, summary.content); | ||
} | ||
if (formatter.flush !== undefined) { | ||
const formatted = formatter.flush(); | ||
if (formatted !== undefined) | ||
this.logger.log(formatted); | ||
} | ||
return success; | ||
} | ||
return success; | ||
} | ||
}; | ||
LintCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [runner_1.Runner, | ||
formatter_loader_1.FormatterLoader, | ||
ymir_1.MessageHandler, | ||
cached_file_system_1.CachedFileSystem]) | ||
], LintCommandRunner); | ||
}; | ||
LintCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [runner_1.Runner, | ||
formatter_loader_1.FormatterLoader, | ||
ymir_1.MessageHandler, | ||
cached_file_system_1.CachedFileSystem]) | ||
], LintCommandRunner); | ||
return LintCommandRunner; | ||
})(); | ||
exports.module = new inversify_1.ContainerModule((bind) => { | ||
@@ -50,0 +54,0 @@ bind(base_1.AbstractCommandRunner).to(LintCommandRunner); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.module = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -10,40 +11,43 @@ const inversify_1 = require("inversify"); | ||
const path = require("path"); | ||
let SaveCommandRunner = class SaveCommandRunner extends base_1.AbstractCommandRunner { | ||
constructor(fs, logger, directories, options) { | ||
super(); | ||
this.fs = fs; | ||
this.logger = logger; | ||
this.directories = directories; | ||
this.options = options; | ||
} | ||
run({ command: _command, ...config }) { | ||
const newContent = utils_1.format({ | ||
...this.options, | ||
...config, | ||
fix: config.fix || undefined, | ||
reportUselessDirectives: config.reportUselessDirectives || undefined, | ||
references: config.references || undefined, | ||
}, "yaml" /* Yaml */); | ||
const filePath = path.join(this.directories.getCurrentDirectory(), '.fimbullinter.yaml'); | ||
if (newContent.trim() === '{}') { | ||
try { | ||
this.fs.remove(filePath); | ||
this.logger.log("Removed '.fimbullinter.yaml'."); | ||
let SaveCommandRunner = /** @class */ (() => { | ||
let SaveCommandRunner = class SaveCommandRunner extends base_1.AbstractCommandRunner { | ||
constructor(fs, logger, directories, options) { | ||
super(); | ||
this.fs = fs; | ||
this.logger = logger; | ||
this.directories = directories; | ||
this.options = options; | ||
} | ||
run({ command: _command, ...config }) { | ||
const newContent = utils_1.format({ | ||
...this.options, | ||
...config, | ||
fix: config.fix || undefined, | ||
reportUselessDirectives: config.reportUselessDirectives || undefined, | ||
references: config.references || undefined, | ||
}, "yaml" /* Yaml */); | ||
const filePath = path.join(this.directories.getCurrentDirectory(), '.fimbullinter.yaml'); | ||
if (newContent.trim() === '{}') { | ||
try { | ||
this.fs.remove(filePath); | ||
this.logger.log("Removed '.fimbullinter.yaml'."); | ||
} | ||
catch { } | ||
} | ||
catch { } | ||
else { | ||
this.fs.writeFile(filePath, newContent); | ||
this.logger.log("Updated '.fimbullinter.yaml'."); | ||
} | ||
return true; | ||
} | ||
else { | ||
this.fs.writeFile(filePath, newContent); | ||
this.logger.log("Updated '.fimbullinter.yaml'."); | ||
} | ||
return true; | ||
} | ||
}; | ||
SaveCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [cached_file_system_1.CachedFileSystem, | ||
ymir_1.MessageHandler, | ||
ymir_1.DirectoryService, | ||
ymir_1.GlobalOptions]) | ||
], SaveCommandRunner); | ||
}; | ||
SaveCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [cached_file_system_1.CachedFileSystem, | ||
ymir_1.MessageHandler, | ||
ymir_1.DirectoryService, | ||
ymir_1.GlobalOptions]) | ||
], SaveCommandRunner); | ||
return SaveCommandRunner; | ||
})(); | ||
exports.module = new inversify_1.ContainerModule((bind) => { | ||
@@ -50,0 +54,0 @@ bind(base_1.AbstractCommandRunner).to(SaveCommandRunner); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.module = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -9,23 +10,26 @@ const configuration_manager_1 = require("../services/configuration-manager"); | ||
const utils_1 = require("../utils"); | ||
let ShowCommandRunner = class ShowCommandRunner extends base_1.AbstractCommandRunner { | ||
constructor(configManager, logger) { | ||
super(); | ||
this.configManager = configManager; | ||
this.logger = logger; | ||
} | ||
run(options) { | ||
const config = options.config === undefined | ||
? this.configManager.find(options.file) | ||
: this.configManager.loadLocalOrResolved(options.config); | ||
if (config === undefined) | ||
throw new ymir_1.ConfigurationError(`Cannot find configuration for '${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)}`); | ||
return true; | ||
} | ||
}; | ||
ShowCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [configuration_manager_1.ConfigurationManager, ymir_1.MessageHandler]) | ||
], ShowCommandRunner); | ||
let ShowCommandRunner = /** @class */ (() => { | ||
let ShowCommandRunner = class ShowCommandRunner extends base_1.AbstractCommandRunner { | ||
constructor(configManager, logger) { | ||
super(); | ||
this.configManager = configManager; | ||
this.logger = logger; | ||
} | ||
run(options) { | ||
const config = options.config === undefined | ||
? this.configManager.find(options.file) | ||
: this.configManager.loadLocalOrResolved(options.config); | ||
if (config === undefined) | ||
throw new ymir_1.ConfigurationError(`Cannot find configuration for '${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)}`); | ||
return true; | ||
} | ||
}; | ||
ShowCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [configuration_manager_1.ConfigurationManager, ymir_1.MessageHandler]) | ||
], ShowCommandRunner); | ||
return ShowCommandRunner; | ||
})(); | ||
exports.module = new inversify_1.ContainerModule((bind) => { | ||
@@ -32,0 +36,0 @@ bind(base_1.AbstractCommandRunner).to(ShowCommandRunner); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.module = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -24,147 +25,153 @@ const inversify_1 = require("inversify"); | ||
}; | ||
let FakeDirectoryService = class FakeDirectoryService { | ||
constructor(realDirectorySerivce) { | ||
this.realDirectorySerivce = realDirectorySerivce; | ||
} | ||
getCurrentDirectory() { | ||
return this.cwd; | ||
} | ||
getRealCurrentDirectory() { | ||
return this.realDirectorySerivce.getCurrentDirectory(); | ||
} | ||
}; | ||
FakeDirectoryService = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.DirectoryService]) | ||
], FakeDirectoryService); | ||
let TestCommandRunner = class TestCommandRunner extends base_1.AbstractCommandRunner { | ||
constructor(runner, fs, logger, directoryService) { | ||
super(); | ||
this.runner = runner; | ||
this.fs = fs; | ||
this.logger = logger; | ||
this.directoryService = directoryService; | ||
} | ||
run(options) { | ||
const currentTypescriptVersion = getNormalizedTypescriptVersion(); | ||
const basedir = this.directoryService.getRealCurrentDirectory(); | ||
let baselineDir; | ||
let root; | ||
let baselinesSeen; | ||
let success = true; | ||
const host = { | ||
checkResult: (file, kind, summary) => { | ||
const relative = path.relative(root, file); | ||
if (relative.startsWith('..' + path.sep)) | ||
throw new ymir_1.ConfigurationError(`Testing file '${file}' outside of '${root}'.`); | ||
const actual = kind === "fix" /* Fix */ ? summary.content : baseline_1.createBaseline(summary); | ||
const baselineFile = `${path.resolve(baselineDir, relative)}.${kind}`; | ||
const end = (pass, text, baselineDiff) => { | ||
this.logger.log(` ${chalk.grey.dim(path.relative(basedir, baselineFile))} ${chalk[pass ? 'green' : 'red'](text)}`); | ||
if (pass) | ||
return true; | ||
if (baselineDiff !== undefined) | ||
this.logger.log(baselineDiff); | ||
success = false; | ||
return !options.bail; | ||
}; | ||
if (kind === "fix" /* Fix */ && summary.fixes === 0) { | ||
if (!this.fs.isFile(baselineFile)) | ||
return true; | ||
let FakeDirectoryService = /** @class */ (() => { | ||
let FakeDirectoryService = class FakeDirectoryService { | ||
constructor(realDirectorySerivce) { | ||
this.realDirectorySerivce = realDirectorySerivce; | ||
} | ||
getCurrentDirectory() { | ||
return this.cwd; | ||
} | ||
getRealCurrentDirectory() { | ||
return this.realDirectorySerivce.getCurrentDirectory(); | ||
} | ||
}; | ||
FakeDirectoryService = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.DirectoryService]) | ||
], FakeDirectoryService); | ||
return FakeDirectoryService; | ||
})(); | ||
let TestCommandRunner = /** @class */ (() => { | ||
let TestCommandRunner = class TestCommandRunner extends base_1.AbstractCommandRunner { | ||
constructor(runner, fs, logger, directoryService) { | ||
super(); | ||
this.runner = runner; | ||
this.fs = fs; | ||
this.logger = logger; | ||
this.directoryService = directoryService; | ||
} | ||
run(options) { | ||
const currentTypescriptVersion = getNormalizedTypescriptVersion(); | ||
const basedir = this.directoryService.getRealCurrentDirectory(); | ||
let baselineDir; | ||
let root; | ||
let baselinesSeen; | ||
let success = true; | ||
const host = { | ||
checkResult: (file, kind, summary) => { | ||
const relative = path.relative(root, file); | ||
if (relative.startsWith('..' + path.sep)) | ||
throw new ymir_1.ConfigurationError(`Testing file '${file}' outside of '${root}'.`); | ||
const actual = kind === "fix" /* Fix */ ? summary.content : baseline_1.createBaseline(summary); | ||
const baselineFile = `${path.resolve(baselineDir, relative)}.${kind}`; | ||
const end = (pass, text, baselineDiff) => { | ||
this.logger.log(` ${chalk.grey.dim(path.relative(basedir, baselineFile))} ${chalk[pass ? 'green' : 'red'](text)}`); | ||
if (pass) | ||
return true; | ||
if (baselineDiff !== undefined) | ||
this.logger.log(baselineDiff); | ||
success = false; | ||
return !options.bail; | ||
}; | ||
if (kind === "fix" /* Fix */ && summary.fixes === 0) { | ||
if (!this.fs.isFile(baselineFile)) | ||
return true; | ||
if (options.updateBaselines) { | ||
this.fs.remove(baselineFile); | ||
return end(true, 'REMOVED'); | ||
} | ||
baselinesSeen.push(utils_1.unixifyPath(baselineFile)); | ||
return end(false, 'EXISTS'); | ||
} | ||
baselinesSeen.push(utils_1.unixifyPath(baselineFile)); | ||
let expected; | ||
try { | ||
expected = this.fs.readFile(baselineFile); | ||
} | ||
catch { | ||
if (!options.updateBaselines) | ||
return end(false, 'MISSING'); | ||
this.fs.createDirectory(path.dirname(baselineFile)); | ||
this.fs.writeFile(baselineFile, actual); | ||
return end(true, 'CREATED'); | ||
} | ||
if (expected === actual) | ||
return end(true, 'PASSED'); | ||
if (options.updateBaselines) { | ||
this.fs.remove(baselineFile); | ||
return end(true, 'REMOVED'); | ||
this.fs.writeFile(baselineFile, actual); | ||
return end(true, 'UPDATED'); | ||
} | ||
baselinesSeen.push(utils_1.unixifyPath(baselineFile)); | ||
return end(false, 'EXISTS'); | ||
} | ||
baselinesSeen.push(utils_1.unixifyPath(baselineFile)); | ||
let expected; | ||
try { | ||
expected = this.fs.readFile(baselineFile); | ||
} | ||
catch { | ||
if (!options.updateBaselines) | ||
return end(false, 'MISSING'); | ||
this.fs.createDirectory(path.dirname(baselineFile)); | ||
this.fs.writeFile(baselineFile, actual); | ||
return end(true, 'CREATED'); | ||
} | ||
if (expected === actual) | ||
return end(true, 'PASSED'); | ||
if (options.updateBaselines) { | ||
this.fs.writeFile(baselineFile, actual); | ||
return end(true, 'UPDATED'); | ||
} | ||
return end(false, 'FAILED', createBaselineDiff(actual, expected)); | ||
}, | ||
}; | ||
const globOptions = { | ||
absolute: true, | ||
cache: {}, | ||
nodir: true, | ||
realpathCache: {}, | ||
statCache: {}, | ||
symlinks: {}, | ||
cwd: basedir, | ||
}; | ||
for (const pattern of options.files) { | ||
for (const testcase of glob.sync(pattern, globOptions)) { | ||
const { typescriptVersion, ...testConfig } = optparse_1.OptionParser.parse(require(testcase), TEST_OPTION_SPEC, { validate: true, context: testcase, exhaustive: true }); | ||
if (typescriptVersion !== undefined && !semver_1.satisfies(currentTypescriptVersion, typescriptVersion)) { | ||
this.logger.log(`${path.relative(basedir, testcase)} ${chalk.yellow(`SKIPPED, requires TypeScript ${typescriptVersion}`)}`); | ||
continue; | ||
} | ||
root = path.dirname(testcase); | ||
baselineDir = buildBaselineDirectoryName(basedir, 'baselines', testcase); | ||
this.logger.log(path.relative(basedir, testcase)); | ||
this.directoryService.cwd = root; | ||
baselinesSeen = []; | ||
if (!this.test(testConfig, host)) | ||
return false; | ||
if (options.exact) { | ||
const remainingGlobOptions = { ...globOptions, cwd: baselineDir, ignore: baselinesSeen }; | ||
for (const unchecked of glob.sync('**', remainingGlobOptions)) { | ||
if (options.updateBaselines) { | ||
this.fs.remove(unchecked); | ||
this.logger.log(` ${chalk.grey.dim(path.relative(basedir, unchecked))} ${chalk.green('REMOVED')}`); | ||
return end(false, 'FAILED', createBaselineDiff(actual, expected)); | ||
}, | ||
}; | ||
const globOptions = { | ||
absolute: true, | ||
cache: {}, | ||
nodir: true, | ||
realpathCache: {}, | ||
statCache: {}, | ||
symlinks: {}, | ||
cwd: basedir, | ||
}; | ||
for (const pattern of options.files) { | ||
for (const testcase of glob.sync(pattern, globOptions)) { | ||
const { typescriptVersion, ...testConfig } = optparse_1.OptionParser.parse(require(testcase), TEST_OPTION_SPEC, { validate: true, context: testcase, exhaustive: true }); | ||
if (typescriptVersion !== undefined && !semver_1.satisfies(currentTypescriptVersion, typescriptVersion)) { | ||
this.logger.log(`${path.relative(basedir, testcase)} ${chalk.yellow(`SKIPPED, requires TypeScript ${typescriptVersion}`)}`); | ||
continue; | ||
} | ||
root = path.dirname(testcase); | ||
baselineDir = buildBaselineDirectoryName(basedir, 'baselines', testcase); | ||
this.logger.log(path.relative(basedir, testcase)); | ||
this.directoryService.cwd = root; | ||
baselinesSeen = []; | ||
if (!this.test(testConfig, host)) | ||
return false; | ||
if (options.exact) { | ||
const remainingGlobOptions = { ...globOptions, cwd: baselineDir, ignore: baselinesSeen }; | ||
for (const unchecked of glob.sync('**', remainingGlobOptions)) { | ||
if (options.updateBaselines) { | ||
this.fs.remove(unchecked); | ||
this.logger.log(` ${chalk.grey.dim(path.relative(basedir, unchecked))} ${chalk.green('REMOVED')}`); | ||
} | ||
else { | ||
this.logger.log(` ${chalk.grey.dim(path.relative(basedir, unchecked))} ${chalk.red('UNCHECKED')}`); | ||
if (options.bail) | ||
return false; | ||
success = false; | ||
} | ||
} | ||
else { | ||
this.logger.log(` ${chalk.grey.dim(path.relative(basedir, unchecked))} ${chalk.red('UNCHECKED')}`); | ||
if (options.bail) | ||
return false; | ||
success = false; | ||
} | ||
} | ||
} | ||
} | ||
return success; | ||
} | ||
return success; | ||
} | ||
test(config, host) { | ||
const lintOptions = { ...config, fix: false }; | ||
const lintResult = Array.from(this.runner.lintCollection(lintOptions)); | ||
let containsFixes = false; | ||
for (const [fileName, summary] of lintResult) { | ||
if (!host.checkResult(fileName, "lint" /* Lint */, summary)) | ||
return false; | ||
containsFixes = containsFixes || summary.findings.some(isFixable); | ||
} | ||
if (config.fix || config.fix === undefined) { | ||
lintOptions.fix = config.fix || true; // fix defaults to true if not specified | ||
const fixResult = containsFixes ? this.runner.lintCollection(lintOptions) : lintResult; | ||
for (const [fileName, summary] of fixResult) | ||
if (!host.checkResult(fileName, "fix" /* Fix */, summary)) | ||
test(config, host) { | ||
const lintOptions = { ...config, fix: false }; | ||
const lintResult = Array.from(this.runner.lintCollection(lintOptions)); | ||
let containsFixes = false; | ||
for (const [fileName, summary] of lintResult) { | ||
if (!host.checkResult(fileName, "lint" /* Lint */, summary)) | ||
return false; | ||
containsFixes = containsFixes || summary.findings.some(isFixable); | ||
} | ||
if (config.fix || config.fix === undefined) { | ||
lintOptions.fix = config.fix || true; // fix defaults to true if not specified | ||
const fixResult = containsFixes ? this.runner.lintCollection(lintOptions) : lintResult; | ||
for (const [fileName, summary] of fixResult) | ||
if (!host.checkResult(fileName, "fix" /* Fix */, summary)) | ||
return false; | ||
} | ||
return true; | ||
} | ||
return true; | ||
} | ||
}; | ||
TestCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [runner_1.Runner, | ||
cached_file_system_1.CachedFileSystem, | ||
ymir_1.MessageHandler, | ||
FakeDirectoryService]) | ||
], TestCommandRunner); | ||
}; | ||
TestCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [runner_1.Runner, | ||
cached_file_system_1.CachedFileSystem, | ||
ymir_1.MessageHandler, | ||
FakeDirectoryService]) | ||
], TestCommandRunner); | ||
return TestCommandRunner; | ||
})(); | ||
function buildBaselineDirectoryName(basedir, baselineDir, testcase) { | ||
@@ -171,0 +178,0 @@ const parts = path.relative(basedir, path.dirname(testcase)).split(path.sep); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.module = void 0; | ||
const tslib_1 = require("tslib"); | ||
const inversify_1 = require("inversify"); | ||
const base_1 = require("./base"); | ||
let ValidateCommandRunner = class ValidateCommandRunner extends base_1.AbstractCommandRunner { | ||
constructor() { | ||
super(); | ||
} | ||
run(_options) { | ||
return true; | ||
} | ||
}; | ||
ValidateCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", []) | ||
], ValidateCommandRunner); | ||
let ValidateCommandRunner = /** @class */ (() => { | ||
let ValidateCommandRunner = class ValidateCommandRunner extends base_1.AbstractCommandRunner { | ||
constructor() { | ||
super(); | ||
} | ||
run(_options) { | ||
return true; | ||
} | ||
}; | ||
ValidateCommandRunner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", []) | ||
], ValidateCommandRunner); | ||
return ValidateCommandRunner; | ||
})(); | ||
exports.module = new inversify_1.ContainerModule((bind) => { | ||
@@ -19,0 +23,0 @@ bind(base_1.AbstractCommandRunner).to(ValidateCommandRunner); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createCoreModule = void 0; | ||
const inversify_1 = require("inversify"); | ||
@@ -4,0 +5,0 @@ const cached_file_system_1 = require("../services/cached-file-system"); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createDefaultModule = void 0; | ||
const inversify_1 = require("inversify"); | ||
@@ -4,0 +5,0 @@ const ymir_1 = require("@fimbul/ymir"); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.applyFixes = void 0; | ||
const stableSort = require("stable"); | ||
@@ -4,0 +5,0 @@ /** |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Linter = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -41,203 +42,206 @@ const ts = require("typescript"); | ||
} | ||
let Linter = class Linter { | ||
constructor(ruleLoader, logger, deprecationHandler, filterFactory) { | ||
this.ruleLoader = ruleLoader; | ||
this.logger = logger; | ||
this.deprecationHandler = deprecationHandler; | ||
this.filterFactory = filterFactory; | ||
} | ||
lintFile(file, config, programOrFactory, options = {}) { | ||
return this.getFindings(file, config, programOrFactory !== undefined && 'getTypeChecker' in programOrFactory | ||
? new StaticProgramFactory(programOrFactory) | ||
: programOrFactory, undefined, options); | ||
} | ||
lintAndFix(file, content, config, updateFile, iterations = 10, programFactory, processor, options = {}) { | ||
let totalFixes = 0; | ||
let findings = this.getFindings(file, config, programFactory, processor, options); | ||
for (let i = 0; i < iterations; ++i) { | ||
if (findings.length === 0) | ||
break; | ||
const fixes = findings.map((f) => f.fix).filter((f) => f !== undefined); | ||
if (fixes.length === 0) { | ||
log('No fixes'); | ||
break; | ||
} | ||
log('Trying to apply %d fixes in %d. iteration', fixes.length, i + 1); | ||
const fixed = fix_1.applyFixes(content, fixes); | ||
log('Applied %d fixes', fixed.fixed); | ||
let newSource; | ||
let fixedRange; | ||
if (processor !== undefined) { | ||
const { transformed, changeRange } = processor.updateSource(fixed.result, fixed.range); | ||
fixedRange = changeRange !== undefined ? changeRange : utils_1.calculateChangeRange(file.text, transformed); | ||
newSource = transformed; | ||
} | ||
else { | ||
newSource = fixed.result; | ||
fixedRange = fixed.range; | ||
} | ||
const updateResult = updateFile(newSource, fixedRange); | ||
if (updateResult === undefined) { | ||
log('Rolling back latest fixes and abort linting'); | ||
if (processor !== undefined) // reset processor state | ||
processor.updateSource(content, utils_1.invertChangeRange(fixed.range)); | ||
break; | ||
} | ||
file = updateResult; | ||
content = fixed.result; | ||
totalFixes += fixed.fixed; | ||
findings = this.getFindings(file, config, programFactory, processor, options); | ||
let Linter = /** @class */ (() => { | ||
let Linter = class Linter { | ||
constructor(ruleLoader, logger, deprecationHandler, filterFactory) { | ||
this.ruleLoader = ruleLoader; | ||
this.logger = logger; | ||
this.deprecationHandler = deprecationHandler; | ||
this.filterFactory = filterFactory; | ||
} | ||
return { | ||
content, | ||
findings, | ||
fixes: totalFixes, | ||
}; | ||
} | ||
// @internal | ||
getFindings(sourceFile, config, programFactory, processor, options) { | ||
if (programFactory !== undefined) | ||
// make sure that all rules get the same Program and CompilerOptions for this run | ||
programFactory = new CachedProgramFactory(programFactory); | ||
let suppressMissingTypeInfoWarning = false; | ||
log('Linting file %s', sourceFile.fileName); | ||
if (programFactory !== undefined) { | ||
const directive = tsutils_1.getCheckJsDirective(sourceFile.text); | ||
if (directive !== undefined | ||
? !directive.enabled | ||
: /\.jsx?/.test(sourceFile.fileName) && !tsutils_1.isCompilerOptionEnabled(programFactory.getCompilerOptions(), 'checkJs')) { | ||
log('Not using type information for this unchecked file'); | ||
programFactory = undefined; | ||
suppressMissingTypeInfoWarning = true; | ||
} | ||
lintFile(file, config, programOrFactory, options = {}) { | ||
return this.getFindings(file, config, programOrFactory !== undefined && 'getTypeChecker' in programOrFactory | ||
? new StaticProgramFactory(programOrFactory) | ||
: programOrFactory, undefined, options); | ||
} | ||
const rules = this.prepareRules(config, sourceFile, programFactory, suppressMissingTypeInfoWarning); | ||
let findings; | ||
if (rules.length === 0) { | ||
log('No active rules'); | ||
if (options.reportUselessDirectives !== undefined) { | ||
findings = this.filterFactory | ||
.create({ sourceFile, getWrappedAst() { return tsutils_1.convertAst(sourceFile).wrapped; }, ruleNames: [] }) | ||
.reportUseless(options.reportUselessDirectives); | ||
log('Found %d useless directives', findings.length); | ||
lintAndFix(file, content, config, updateFile, iterations = 10, programFactory, processor, options = {}) { | ||
let totalFixes = 0; | ||
let findings = this.getFindings(file, config, programFactory, processor, options); | ||
for (let i = 0; i < iterations; ++i) { | ||
if (findings.length === 0) | ||
break; | ||
const fixes = findings.map((f) => f.fix).filter((f) => f !== undefined); | ||
if (fixes.length === 0) { | ||
log('No fixes'); | ||
break; | ||
} | ||
log('Trying to apply %d fixes in %d. iteration', fixes.length, i + 1); | ||
const fixed = fix_1.applyFixes(content, fixes); | ||
log('Applied %d fixes', fixed.fixed); | ||
let newSource; | ||
let fixedRange; | ||
if (processor !== undefined) { | ||
const { transformed, changeRange } = processor.updateSource(fixed.result, fixed.range); | ||
fixedRange = changeRange !== undefined ? changeRange : utils_1.calculateChangeRange(file.text, transformed); | ||
newSource = transformed; | ||
} | ||
else { | ||
newSource = fixed.result; | ||
fixedRange = fixed.range; | ||
} | ||
const updateResult = updateFile(newSource, fixedRange); | ||
if (updateResult === undefined) { | ||
log('Rolling back latest fixes and abort linting'); | ||
if (processor !== undefined) // reset processor state | ||
processor.updateSource(content, utils_1.invertChangeRange(fixed.range)); | ||
break; | ||
} | ||
file = updateResult; | ||
content = fixed.result; | ||
totalFixes += fixed.fixed; | ||
findings = this.getFindings(file, config, programFactory, processor, options); | ||
} | ||
else { | ||
findings = []; | ||
} | ||
return { | ||
content, | ||
findings, | ||
fixes: totalFixes, | ||
}; | ||
} | ||
findings = this.applyRules(sourceFile, programFactory, rules, config.settings, options); | ||
return processor === undefined ? findings : processor.postprocess(findings); | ||
} | ||
prepareRules(config, sourceFile, programFactory, noWarn) { | ||
const rules = []; | ||
for (const [ruleName, { options, severity, rulesDirectories, rule }] of config.rules) { | ||
if (severity === 'off') | ||
continue; | ||
const ctor = this.ruleLoader.loadRule(rule, rulesDirectories); | ||
if (ctor === undefined) | ||
continue; | ||
if (ctor.deprecated) | ||
this.deprecationHandler.handle("rule" /* Rule */, ruleName, typeof ctor.deprecated === 'string' ? ctor.deprecated : undefined); | ||
if (programFactory === undefined && ctor.requiresTypeInformation) { | ||
if (noWarn) { | ||
log('Rule %s requires type information', ruleName); | ||
// @internal | ||
getFindings(sourceFile, config, programFactory, processor, options) { | ||
if (programFactory !== undefined) | ||
// make sure that all rules get the same Program and CompilerOptions for this run | ||
programFactory = new CachedProgramFactory(programFactory); | ||
let suppressMissingTypeInfoWarning = false; | ||
log('Linting file %s', sourceFile.fileName); | ||
if (programFactory !== undefined) { | ||
const directive = tsutils_1.getCheckJsDirective(sourceFile.text); | ||
if (directive !== undefined | ||
? !directive.enabled | ||
: /\.jsx?/.test(sourceFile.fileName) && !tsutils_1.isCompilerOptionEnabled(programFactory.getCompilerOptions(), 'checkJs')) { | ||
log('Not using type information for this unchecked file'); | ||
programFactory = undefined; | ||
suppressMissingTypeInfoWarning = true; | ||
} | ||
} | ||
const rules = this.prepareRules(config, sourceFile, programFactory, suppressMissingTypeInfoWarning); | ||
let findings; | ||
if (rules.length === 0) { | ||
log('No active rules'); | ||
if (options.reportUselessDirectives !== undefined) { | ||
findings = this.filterFactory | ||
.create({ sourceFile, getWrappedAst() { return tsutils_1.convertAst(sourceFile).wrapped; }, ruleNames: [] }) | ||
.reportUseless(options.reportUselessDirectives); | ||
log('Found %d useless directives', findings.length); | ||
} | ||
else { | ||
this.logger.warn(`Rule '${ruleName}' requires type information.`); | ||
findings = []; | ||
} | ||
continue; | ||
} | ||
if (ctor.supports !== undefined) { | ||
const supports = ctor.supports(sourceFile, { | ||
get program() { return programFactory && programFactory.getProgram(); }, | ||
get compilerOptions() { return programFactory && programFactory.getCompilerOptions(); }, | ||
options, | ||
settings: config.settings, | ||
}); | ||
if (supports !== true) { | ||
if (!supports) { | ||
log(`Rule %s does not support this file`, ruleName); | ||
findings = this.applyRules(sourceFile, programFactory, rules, config.settings, options); | ||
return processor === undefined ? findings : processor.postprocess(findings); | ||
} | ||
prepareRules(config, sourceFile, programFactory, noWarn) { | ||
const rules = []; | ||
for (const [ruleName, { options, severity, rulesDirectories, rule }] of config.rules) { | ||
if (severity === 'off') | ||
continue; | ||
const ctor = this.ruleLoader.loadRule(rule, rulesDirectories); | ||
if (ctor === undefined) | ||
continue; | ||
if (ctor.deprecated) | ||
this.deprecationHandler.handle("rule" /* Rule */, ruleName, typeof ctor.deprecated === 'string' ? ctor.deprecated : undefined); | ||
if (programFactory === undefined && ctor.requiresTypeInformation) { | ||
if (noWarn) { | ||
log('Rule %s requires type information', ruleName); | ||
} | ||
else { | ||
log(`Rule %s does not support this file: %s`, ruleName, supports); | ||
this.logger.warn(`Rule '${ruleName}' requires type information.`); | ||
} | ||
continue; | ||
} | ||
if (ctor.supports !== undefined) { | ||
const supports = ctor.supports(sourceFile, { | ||
get program() { return programFactory && programFactory.getProgram(); }, | ||
get compilerOptions() { return programFactory && programFactory.getCompilerOptions(); }, | ||
options, | ||
settings: config.settings, | ||
}); | ||
if (supports !== true) { | ||
if (!supports) { | ||
log(`Rule %s does not support this file`, ruleName); | ||
} | ||
else { | ||
log(`Rule %s does not support this file: %s`, ruleName, supports); | ||
} | ||
continue; | ||
} | ||
} | ||
rules.push({ ruleName, options, severity, ctor }); | ||
} | ||
rules.push({ ruleName, options, severity, ctor }); | ||
return rules; | ||
} | ||
return rules; | ||
} | ||
applyRules(sourceFile, programFactory, rules, settings, options) { | ||
const result = []; | ||
let findingFilter; | ||
let ruleName; | ||
let severity; | ||
let ctor; | ||
let convertedAst; | ||
const getFindingFilter = () => { | ||
return findingFilter || | ||
(findingFilter = this.filterFactory.create({ sourceFile, getWrappedAst, ruleNames: rules.map((r) => r.ruleName) })); | ||
}; | ||
const addFinding = (pos, end, message, fix) => { | ||
const finding = { | ||
ruleName, | ||
severity, | ||
message, | ||
start: { | ||
position: pos, | ||
...ts.getLineAndCharacterOfPosition(sourceFile, pos), | ||
}, | ||
end: { | ||
position: end, | ||
...ts.getLineAndCharacterOfPosition(sourceFile, end), | ||
}, | ||
fix: fix === undefined | ||
? undefined | ||
: !Array.isArray(fix) | ||
? { replacements: [fix] } | ||
: fix.length === 0 | ||
? undefined | ||
: { replacements: fix }, | ||
applyRules(sourceFile, programFactory, rules, settings, options) { | ||
const result = []; | ||
let findingFilter; | ||
let ruleName; | ||
let severity; | ||
let ctor; | ||
let convertedAst; | ||
const getFindingFilter = () => { | ||
return findingFilter || | ||
(findingFilter = this.filterFactory.create({ sourceFile, getWrappedAst, ruleNames: rules.map((r) => r.ruleName) })); | ||
}; | ||
if (getFindingFilter().filter(finding)) | ||
result.push(finding); | ||
}; | ||
const context = { | ||
addFinding, | ||
getFlatAst, | ||
getWrappedAst, | ||
get program() { return programFactory && programFactory.getProgram(); }, | ||
get compilerOptions() { return programFactory && programFactory.getCompilerOptions(); }, | ||
sourceFile, | ||
settings, | ||
options: undefined, | ||
}; | ||
for ({ ruleName, severity, ctor, options: context.options } of rules) { | ||
log('Executing rule %s', ruleName); | ||
new ctor(context).apply(); | ||
const addFinding = (pos, end, message, fix) => { | ||
const finding = { | ||
ruleName, | ||
severity, | ||
message, | ||
start: { | ||
position: pos, | ||
...ts.getLineAndCharacterOfPosition(sourceFile, pos), | ||
}, | ||
end: { | ||
position: end, | ||
...ts.getLineAndCharacterOfPosition(sourceFile, end), | ||
}, | ||
fix: fix === undefined | ||
? undefined | ||
: !Array.isArray(fix) | ||
? { replacements: [fix] } | ||
: fix.length === 0 | ||
? undefined | ||
: { replacements: fix }, | ||
}; | ||
if (getFindingFilter().filter(finding)) | ||
result.push(finding); | ||
}; | ||
const context = { | ||
addFinding, | ||
getFlatAst, | ||
getWrappedAst, | ||
get program() { return programFactory && programFactory.getProgram(); }, | ||
get compilerOptions() { return programFactory && programFactory.getCompilerOptions(); }, | ||
sourceFile, | ||
settings, | ||
options: undefined, | ||
}; | ||
for ({ ruleName, severity, ctor, options: context.options } of rules) { | ||
log('Executing rule %s', ruleName); | ||
new ctor(context).apply(); | ||
} | ||
log('Found %d findings', result.length); | ||
if (options.reportUselessDirectives !== undefined) { | ||
const useless = getFindingFilter().reportUseless(options.reportUselessDirectives); | ||
log('Found %d useless directives', useless.length); | ||
result.push(...useless); | ||
} | ||
return result; | ||
function getFlatAst() { | ||
return (convertedAst || (convertedAst = tsutils_1.convertAst(sourceFile))).flat; | ||
} | ||
function getWrappedAst() { | ||
return (convertedAst || (convertedAst = tsutils_1.convertAst(sourceFile))).wrapped; | ||
} | ||
} | ||
log('Found %d findings', result.length); | ||
if (options.reportUselessDirectives !== undefined) { | ||
const useless = getFindingFilter().reportUseless(options.reportUselessDirectives); | ||
log('Found %d useless directives', useless.length); | ||
result.push(...useless); | ||
} | ||
return result; | ||
function getFlatAst() { | ||
return (convertedAst || (convertedAst = tsutils_1.convertAst(sourceFile))).flat; | ||
} | ||
function getWrappedAst() { | ||
return (convertedAst || (convertedAst = tsutils_1.convertAst(sourceFile))).wrapped; | ||
} | ||
} | ||
}; | ||
Linter = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [rule_loader_1.RuleLoader, | ||
ymir_1.MessageHandler, | ||
ymir_1.DeprecationHandler, | ||
ymir_1.FindingFilterFactory]) | ||
], Linter); | ||
}; | ||
Linter = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [rule_loader_1.RuleLoader, | ||
ymir_1.MessageHandler, | ||
ymir_1.DeprecationHandler, | ||
ymir_1.FindingFilterFactory]) | ||
], Linter); | ||
return Linter; | ||
})(); | ||
exports.Linter = Linter; | ||
//# sourceMappingURL=linter.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.OptionParser = void 0; | ||
const debug = require("debug"); | ||
@@ -4,0 +5,0 @@ const ymir_1 = require("@fimbul/ymir"); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ProjectHost = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -11,237 +12,240 @@ const ts = require("typescript"); | ||
const additionalExtensions = ['.json']; | ||
class ProjectHost { | ||
constructor(cwd, config, fs, configManager, processorLoader) { | ||
this.cwd = cwd; | ||
this.config = config; | ||
this.fs = fs; | ||
this.configManager = configManager; | ||
this.processorLoader = processorLoader; | ||
this.reverseMap = new Map(); | ||
this.files = []; | ||
this.directoryEntries = new Map(); | ||
this.processedFiles = new Map(); | ||
this.sourceFileCache = new Map(); | ||
this.fileContent = new Map(); | ||
this.tsconfigCache = new Map(); | ||
this.commandLineCache = new Map(); | ||
this.parseConfigHost = { | ||
useCaseSensitiveFileNames: this.useCaseSensitiveFileNames(), | ||
readDirectory: (rootDir, extensions, excludes, includes, depth) => this.readDirectory(rootDir, extensions, excludes, includes, depth), | ||
fileExists: (f) => this.fileExists(f), | ||
readFile: (f) => this.readFile(f), | ||
}; | ||
this.getCanonicalFileName = ts.sys.useCaseSensitiveFileNames ? (f) => f : (f) => f.toLowerCase(); | ||
this.moduleResolutionCache = ts.createModuleResolutionCache(this.cwd, this.getCanonicalFileName); | ||
this.compilerOptions = {}; | ||
this.getDefaultLibFileName = ts.getDefaultLibFilePath; | ||
this.realpath = this.fs.realpath === undefined ? undefined : (fileName) => this.fs.realpath(fileName); | ||
} | ||
getProcessedFileInfo(fileName) { | ||
return this.processedFiles.get(fileName); | ||
} | ||
readDirectory(rootDir, extensions, excludes, includes, depth) { | ||
return ts.matchFiles(rootDir, extensions, excludes, includes, this.useCaseSensitiveFileNames(), this.cwd, depth, (dir) => utils_1.resolveCachedResult(this.directoryEntries, dir, this.processDirectory), (f) => this.safeRealpath(f)); | ||
} | ||
/** | ||
* Try to find and load the configuration for a file. | ||
* If it fails, just continue as if there was no config. | ||
* This may happen during project setup if there is an invalid config file anywhere in a scanned folder. | ||
*/ | ||
tryFindConfig(file) { | ||
try { | ||
return this.configManager.find(file); | ||
let ProjectHost = /** @class */ (() => { | ||
class ProjectHost { | ||
constructor(cwd, config, fs, configManager, processorLoader) { | ||
this.cwd = cwd; | ||
this.config = config; | ||
this.fs = fs; | ||
this.configManager = configManager; | ||
this.processorLoader = processorLoader; | ||
this.reverseMap = new Map(); | ||
this.files = []; | ||
this.directoryEntries = new Map(); | ||
this.processedFiles = new Map(); | ||
this.sourceFileCache = new Map(); | ||
this.fileContent = new Map(); | ||
this.tsconfigCache = new Map(); | ||
this.commandLineCache = new Map(); | ||
this.parseConfigHost = { | ||
useCaseSensitiveFileNames: this.useCaseSensitiveFileNames(), | ||
readDirectory: (rootDir, extensions, excludes, includes, depth) => this.readDirectory(rootDir, extensions, excludes, includes, depth), | ||
fileExists: (f) => this.fileExists(f), | ||
readFile: (f) => this.readFile(f), | ||
}; | ||
this.getCanonicalFileName = ts.sys.useCaseSensitiveFileNames ? (f) => f : (f) => f.toLowerCase(); | ||
this.moduleResolutionCache = ts.createModuleResolutionCache(this.cwd, this.getCanonicalFileName); | ||
this.compilerOptions = {}; | ||
this.getDefaultLibFileName = ts.getDefaultLibFilePath; | ||
this.realpath = this.fs.realpath === undefined ? undefined : (fileName) => this.fs.realpath(fileName); | ||
} | ||
catch (e) { | ||
log("Error while loading configuration for '%s': %s", file, e.message); | ||
return; | ||
getProcessedFileInfo(fileName) { | ||
return this.processedFiles.get(fileName); | ||
} | ||
} | ||
processDirectory(dir) { | ||
const files = []; | ||
const directories = []; | ||
const result = { files, directories }; | ||
let entries; | ||
try { | ||
entries = this.fs.readDirectory(dir); | ||
readDirectory(rootDir, extensions, excludes, includes, depth) { | ||
return ts.matchFiles(rootDir, extensions, excludes, includes, this.useCaseSensitiveFileNames(), this.cwd, depth, (dir) => utils_1.resolveCachedResult(this.directoryEntries, dir, this.processDirectory), (f) => this.safeRealpath(f)); | ||
} | ||
catch { | ||
return result; | ||
/** | ||
* Try to find and load the configuration for a file. | ||
* If it fails, just continue as if there was no config. | ||
* This may happen during project setup if there is an invalid config file anywhere in a scanned folder. | ||
*/ | ||
tryFindConfig(file) { | ||
try { | ||
return this.configManager.find(file); | ||
} | ||
catch (e) { | ||
log("Error while loading configuration for '%s': %s", file, e.message); | ||
return; | ||
} | ||
} | ||
for (const entry of entries) { | ||
const fileName = `${dir}/${entry.name}`; | ||
switch (entry.kind) { | ||
case 1 /* File */: { | ||
if (!utils_1.hasSupportedExtension(fileName, additionalExtensions)) { | ||
const c = this.config || this.tryFindConfig(fileName); | ||
const processor = c && this.configManager.getProcessor(c, fileName); | ||
if (processor) { | ||
const ctor = this.processorLoader.loadProcessor(processor); | ||
const newName = fileName + | ||
ctor.getSuffixForFile({ | ||
fileName, | ||
getSettings: () => this.configManager.getSettings(c, fileName), | ||
readFile: () => this.fs.readFile(fileName), | ||
}); | ||
if (utils_1.hasSupportedExtension(newName, additionalExtensions)) { | ||
files.push(newName); | ||
this.reverseMap.set(newName, fileName); | ||
break; | ||
processDirectory(dir) { | ||
const files = []; | ||
const directories = []; | ||
const result = { files, directories }; | ||
let entries; | ||
try { | ||
entries = this.fs.readDirectory(dir); | ||
} | ||
catch { | ||
return result; | ||
} | ||
for (const entry of entries) { | ||
const fileName = `${dir}/${entry.name}`; | ||
switch (entry.kind) { | ||
case 1 /* File */: { | ||
if (!utils_1.hasSupportedExtension(fileName, additionalExtensions)) { | ||
const c = this.config || this.tryFindConfig(fileName); | ||
const processor = c && this.configManager.getProcessor(c, fileName); | ||
if (processor) { | ||
const ctor = this.processorLoader.loadProcessor(processor); | ||
const newName = fileName + | ||
ctor.getSuffixForFile({ | ||
fileName, | ||
getSettings: () => this.configManager.getSettings(c, fileName), | ||
readFile: () => this.fs.readFile(fileName), | ||
}); | ||
if (utils_1.hasSupportedExtension(newName, additionalExtensions)) { | ||
files.push(newName); | ||
this.reverseMap.set(newName, fileName); | ||
break; | ||
} | ||
} | ||
} | ||
files.push(fileName); | ||
this.files.push(fileName); | ||
break; | ||
} | ||
files.push(fileName); | ||
this.files.push(fileName); | ||
break; | ||
case 2 /* Directory */: | ||
directories.push(fileName); | ||
} | ||
} | ||
return result; | ||
} | ||
fileExists(file) { | ||
switch (this.fs.getKind(file)) { | ||
case 2 /* Directory */: | ||
directories.push(fileName); | ||
case 3 /* Other */: | ||
return false; | ||
case 1 /* File */: | ||
return true; | ||
default: | ||
return utils_1.hasSupportedExtension(file, additionalExtensions) && this.getFileSystemFile(file) !== undefined; | ||
} | ||
} | ||
return result; | ||
} | ||
fileExists(file) { | ||
switch (this.fs.getKind(file)) { | ||
case 2 /* Directory */: | ||
case 3 /* Other */: | ||
return false; | ||
case 1 /* File */: | ||
return true; | ||
default: | ||
return utils_1.hasSupportedExtension(file, additionalExtensions) && this.getFileSystemFile(file) !== undefined; | ||
directoryExists(dir) { | ||
return this.fs.isDirectory(dir); | ||
} | ||
} | ||
directoryExists(dir) { | ||
return this.fs.isDirectory(dir); | ||
} | ||
getFileSystemFile(file) { | ||
if (this.files.includes(file)) | ||
return file; | ||
const reverse = this.reverseMap.get(file); | ||
if (reverse !== undefined) | ||
return reverse; | ||
const dirname = path.posix.dirname(file); | ||
if (this.directoryEntries.has(dirname)) | ||
return; | ||
if (this.fs.isFile(file)) | ||
return file; | ||
this.directoryEntries.set(dirname, this.processDirectory(dirname)); | ||
return this.getFileSystemFile(file); | ||
} | ||
readFile(file) { | ||
return utils_1.resolveCachedResult(this.fileContent, file, (f) => this.fs.readFile(f)); | ||
} | ||
readProcessedFile(file) { | ||
const realFile = this.getFileSystemFile(file); | ||
if (realFile === undefined) | ||
return; | ||
let content = this.fs.readFile(realFile); | ||
const config = this.config || this.tryFindConfig(realFile); | ||
if (config === undefined) | ||
getFileSystemFile(file) { | ||
if (this.files.includes(file)) | ||
return file; | ||
const reverse = this.reverseMap.get(file); | ||
if (reverse !== undefined) | ||
return reverse; | ||
const dirname = path.posix.dirname(file); | ||
if (this.directoryEntries.has(dirname)) | ||
return; | ||
if (this.fs.isFile(file)) | ||
return file; | ||
this.directoryEntries.set(dirname, this.processDirectory(dirname)); | ||
return this.getFileSystemFile(file); | ||
} | ||
readFile(file) { | ||
return utils_1.resolveCachedResult(this.fileContent, file, (f) => this.fs.readFile(f)); | ||
} | ||
readProcessedFile(file) { | ||
const realFile = this.getFileSystemFile(file); | ||
if (realFile === undefined) | ||
return; | ||
let content = this.fs.readFile(realFile); | ||
const config = this.config || this.tryFindConfig(realFile); | ||
if (config === undefined) | ||
return content; | ||
const processorPath = this.configManager.getProcessor(config, realFile); | ||
if (processorPath === undefined) | ||
return content; | ||
const ctor = this.processorLoader.loadProcessor(processorPath); | ||
const processor = new ctor({ | ||
source: content, | ||
sourceFileName: realFile, | ||
targetFileName: file, | ||
settings: this.configManager.getSettings(config, realFile), | ||
}); | ||
this.processedFiles.set(file, { | ||
processor, | ||
originalContent: content, | ||
originalName: realFile, | ||
}); | ||
content = processor.preprocess(); | ||
return content; | ||
const processorPath = this.configManager.getProcessor(config, realFile); | ||
if (processorPath === undefined) | ||
return content; | ||
const ctor = this.processorLoader.loadProcessor(processorPath); | ||
const processor = new ctor({ | ||
source: content, | ||
sourceFileName: realFile, | ||
targetFileName: file, | ||
settings: this.configManager.getSettings(config, realFile), | ||
}); | ||
this.processedFiles.set(file, { | ||
processor, | ||
originalContent: content, | ||
originalName: realFile, | ||
}); | ||
content = processor.preprocess(); | ||
return content; | ||
} | ||
writeFile() { } | ||
useCaseSensitiveFileNames() { | ||
return ts.sys.useCaseSensitiveFileNames; | ||
} | ||
getNewLine() { | ||
return this.compilerOptions.newLine === ts.NewLineKind.CarriageReturnLineFeed ? '\r\n' : '\n'; | ||
} | ||
safeRealpath(f) { | ||
if (this.realpath !== undefined) { | ||
try { | ||
return this.realpath(f); | ||
} | ||
writeFile() { } | ||
useCaseSensitiveFileNames() { | ||
return ts.sys.useCaseSensitiveFileNames; | ||
} | ||
getNewLine() { | ||
return this.compilerOptions.newLine === ts.NewLineKind.CarriageReturnLineFeed ? '\r\n' : '\n'; | ||
} | ||
safeRealpath(f) { | ||
if (this.realpath !== undefined) { | ||
try { | ||
return this.realpath(f); | ||
} | ||
catch { } | ||
} | ||
catch { } | ||
return f; | ||
} | ||
return f; | ||
getCurrentDirectory() { | ||
return this.cwd; | ||
} | ||
getDirectories(dir) { | ||
const cached = this.directoryEntries.get(dir); | ||
if (cached !== undefined) | ||
return cached.directories.map((d) => path.posix.basename(d)); | ||
return utils_1.mapDefined(this.fs.readDirectory(dir), (entry) => entry.kind === 2 /* Directory */ ? path.join(dir, entry.name) : undefined); | ||
} | ||
getSourceFile(fileName, languageVersion) { | ||
return utils_1.resolveCachedResult(this.sourceFileCache, fileName, () => { | ||
const content = this.readProcessedFile(fileName); | ||
return content !== undefined ? ts.createSourceFile(fileName, content, languageVersion, true) : undefined; | ||
}); | ||
} | ||
createProgram(rootNames, options, oldProgram, projectReferences) { | ||
options = { ...options, suppressOutputPathCheck: true }; | ||
this.compilerOptions = options; | ||
this.moduleResolutionCache = ts.createModuleResolutionCache(this.cwd, this.getCanonicalFileName, options); | ||
return ts.createProgram({ rootNames, options, oldProgram, projectReferences, host: this }); | ||
} | ||
updateSourceFile(sourceFile) { | ||
this.sourceFileCache.set(sourceFile.fileName, sourceFile); | ||
} | ||
updateProgram(program) { | ||
return ts.createProgram({ | ||
rootNames: program.getRootFileNames(), | ||
options: program.getCompilerOptions(), | ||
oldProgram: program, | ||
projectReferences: program.getProjectReferences(), | ||
host: this, | ||
}); | ||
} | ||
onReleaseOldSourceFile(sourceFile) { | ||
// this is only called for paths that are no longer referenced | ||
// it's safe to remove the cache entry completely because it won't be called with updated SourceFiles | ||
this.uncacheFile(sourceFile.fileName); | ||
} | ||
uncacheFile(fileName) { | ||
this.sourceFileCache.delete(fileName); | ||
this.processedFiles.delete(fileName); | ||
} | ||
getParsedCommandLine(fileName) { | ||
return utils_1.resolveCachedResult(this.commandLineCache, fileName, this.parseConfigFile); | ||
} | ||
parseConfigFile(fileName) { | ||
// Note to future self: it's highly unlikely that a tsconfig of a project reference is used as base config for another tsconfig. | ||
// Therefore it doesn't make such sense to read or write the tsconfigCache here. | ||
const sourceFile = this.getSourceFile(fileName, ts.ScriptTarget.JSON); | ||
if (sourceFile === undefined) | ||
return; | ||
return ts.parseJsonSourceFileConfigFileContent(sourceFile, this.parseConfigHost, path.dirname(fileName), undefined, fileName, undefined, undefined, this.tsconfigCache); | ||
} | ||
resolveModuleNames(names, file, _, reference) { | ||
const seen = new Map(); | ||
const resolve = (name) => ts.resolveModuleName(name, file, this.compilerOptions, this, this.moduleResolutionCache, reference).resolvedModule; | ||
return names.map((name) => utils_1.resolveCachedResult(seen, name, resolve)); | ||
} | ||
} | ||
getCurrentDirectory() { | ||
return this.cwd; | ||
} | ||
getDirectories(dir) { | ||
const cached = this.directoryEntries.get(dir); | ||
if (cached !== undefined) | ||
return cached.directories.map((d) => path.posix.basename(d)); | ||
return utils_1.mapDefined(this.fs.readDirectory(dir), (entry) => entry.kind === 2 /* Directory */ ? path.join(dir, entry.name) : undefined); | ||
} | ||
getSourceFile(fileName, languageVersion) { | ||
return utils_1.resolveCachedResult(this.sourceFileCache, fileName, () => { | ||
const content = this.readProcessedFile(fileName); | ||
return content !== undefined ? ts.createSourceFile(fileName, content, languageVersion, true) : undefined; | ||
}); | ||
} | ||
createProgram(rootNames, options, oldProgram, projectReferences) { | ||
options = { ...options, suppressOutputPathCheck: true }; | ||
this.compilerOptions = options; | ||
this.moduleResolutionCache = ts.createModuleResolutionCache(this.cwd, this.getCanonicalFileName, options); | ||
return ts.createProgram({ rootNames, options, oldProgram, projectReferences, host: this }); | ||
} | ||
updateSourceFile(sourceFile) { | ||
this.sourceFileCache.set(sourceFile.fileName, sourceFile); | ||
} | ||
updateProgram(program) { | ||
return ts.createProgram({ | ||
rootNames: program.getRootFileNames(), | ||
options: program.getCompilerOptions(), | ||
oldProgram: program, | ||
projectReferences: program.getProjectReferences(), | ||
host: this, | ||
}); | ||
} | ||
onReleaseOldSourceFile(sourceFile) { | ||
// this is only called for paths that are no longer referenced | ||
// it's safe to remove the cache entry completely because it won't be called with updated SourceFiles | ||
this.uncacheFile(sourceFile.fileName); | ||
} | ||
uncacheFile(fileName) { | ||
this.sourceFileCache.delete(fileName); | ||
this.processedFiles.delete(fileName); | ||
} | ||
getParsedCommandLine(fileName) { | ||
return utils_1.resolveCachedResult(this.commandLineCache, fileName, this.parseConfigFile); | ||
} | ||
parseConfigFile(fileName) { | ||
// Note to future self: it's highly unlikely that a tsconfig of a project reference is used as base config for another tsconfig. | ||
// Therefore it doesn't make such sense to read or write the tsconfigCache here. | ||
const sourceFile = this.getSourceFile(fileName, ts.ScriptTarget.JSON); | ||
if (sourceFile === undefined) | ||
return; | ||
return ts.parseJsonSourceFileConfigFileContent(sourceFile, this.parseConfigHost, path.dirname(fileName), undefined, fileName, undefined, undefined, this.tsconfigCache); | ||
} | ||
resolveModuleNames(names, file, _, reference) { | ||
const seen = new Map(); | ||
const resolve = (name) => ts.resolveModuleName(name, file, this.compilerOptions, this, this.moduleResolutionCache, reference).resolvedModule; | ||
return names.map((name) => utils_1.resolveCachedResult(seen, name, resolve)); | ||
} | ||
} | ||
tslib_1.__decorate([ | ||
bind_decorator_1.bind, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", Object) | ||
], ProjectHost.prototype, "processDirectory", null); | ||
tslib_1.__decorate([ | ||
bind_decorator_1.bind, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", void 0) | ||
], ProjectHost.prototype, "parseConfigFile", null); | ||
tslib_1.__decorate([ | ||
bind_decorator_1.bind, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", Object) | ||
], ProjectHost.prototype, "processDirectory", null); | ||
tslib_1.__decorate([ | ||
bind_decorator_1.bind, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", void 0) | ||
], ProjectHost.prototype, "parseConfigFile", null); | ||
return ProjectHost; | ||
})(); | ||
exports.ProjectHost = ProjectHost; | ||
//# sourceMappingURL=project-host.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Runner = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -19,75 +20,144 @@ const linter_1 = require("./linter"); | ||
const log = debug('wotan:runner'); | ||
let Runner = class Runner { | ||
constructor(fs, configManager, linter, processorLoader, directories, logger, filterFactory) { | ||
this.fs = fs; | ||
this.configManager = configManager; | ||
this.linter = linter; | ||
this.processorLoader = processorLoader; | ||
this.directories = directories; | ||
this.logger = logger; | ||
this.filterFactory = filterFactory; | ||
} | ||
lintCollection(options) { | ||
const config = options.config !== undefined ? this.configManager.loadLocalOrResolved(options.config) : undefined; | ||
const cwd = this.directories.getCurrentDirectory(); | ||
const files = options.files.map((pattern) => ({ hasMagic: glob.hasMagic(pattern), normalized: Array.from(normalize_glob_1.normalizeGlob(pattern, cwd)) })); | ||
const exclude = utils_1.flatMap(options.exclude, (pattern) => normalize_glob_1.normalizeGlob(pattern, cwd)); | ||
const linterOptions = { | ||
reportUselessDirectives: options.reportUselessDirectives | ||
? options.reportUselessDirectives === true | ||
? 'error' | ||
: options.reportUselessDirectives | ||
: undefined, | ||
}; | ||
if (options.project.length === 0 && options.files.length !== 0) | ||
return this.lintFiles({ ...options, files, exclude }, config, linterOptions); | ||
return this.lintProject({ ...options, files, exclude }, config, linterOptions); | ||
} | ||
*lintProject(options, config, linterOptions) { | ||
const processorHost = new project_host_1.ProjectHost(this.directories.getCurrentDirectory(), config, this.fs, this.configManager, this.processorLoader); | ||
for (let { files, program } of this.getFilesAndProgram(options.project, options.files, options.exclude, processorHost, options.references)) { | ||
let invalidatedProgram = false; | ||
const factory = { | ||
getCompilerOptions() { | ||
return program.getCompilerOptions(); | ||
}, | ||
getProgram() { | ||
if (invalidatedProgram) { | ||
log('updating invalidated program'); | ||
program = processorHost.updateProgram(program); | ||
invalidatedProgram = false; | ||
let Runner = /** @class */ (() => { | ||
let Runner = class Runner { | ||
constructor(fs, configManager, linter, processorLoader, directories, logger, filterFactory) { | ||
this.fs = fs; | ||
this.configManager = configManager; | ||
this.linter = linter; | ||
this.processorLoader = processorLoader; | ||
this.directories = directories; | ||
this.logger = logger; | ||
this.filterFactory = filterFactory; | ||
} | ||
lintCollection(options) { | ||
const config = options.config !== undefined ? this.configManager.loadLocalOrResolved(options.config) : undefined; | ||
const cwd = this.directories.getCurrentDirectory(); | ||
const files = options.files.map((pattern) => ({ hasMagic: glob.hasMagic(pattern), normalized: Array.from(normalize_glob_1.normalizeGlob(pattern, cwd)) })); | ||
const exclude = utils_1.flatMap(options.exclude, (pattern) => normalize_glob_1.normalizeGlob(pattern, cwd)); | ||
const linterOptions = { | ||
reportUselessDirectives: options.reportUselessDirectives | ||
? options.reportUselessDirectives === true | ||
? 'error' | ||
: options.reportUselessDirectives | ||
: undefined, | ||
}; | ||
if (options.project.length === 0 && options.files.length !== 0) | ||
return this.lintFiles({ ...options, files, exclude }, config, linterOptions); | ||
return this.lintProject({ ...options, files, exclude }, config, linterOptions); | ||
} | ||
*lintProject(options, config, linterOptions) { | ||
const processorHost = new project_host_1.ProjectHost(this.directories.getCurrentDirectory(), config, this.fs, this.configManager, this.processorLoader); | ||
for (let { files, program } of this.getFilesAndProgram(options.project, options.files, options.exclude, processorHost, options.references)) { | ||
let invalidatedProgram = false; | ||
const factory = { | ||
getCompilerOptions() { | ||
return program.getCompilerOptions(); | ||
}, | ||
getProgram() { | ||
if (invalidatedProgram) { | ||
log('updating invalidated program'); | ||
program = processorHost.updateProgram(program); | ||
invalidatedProgram = false; | ||
} | ||
return program; | ||
}, | ||
}; | ||
for (const file of files) { | ||
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.reduce(config, originalName); | ||
if (effectiveConfig === undefined) | ||
continue; | ||
let sourceFile = program.getSourceFile(file); | ||
const originalContent = mapped === undefined ? sourceFile.text : mapped.originalContent; | ||
let summary; | ||
const fix = shouldFix(sourceFile, options, originalName); | ||
if (fix) { | ||
summary = this.linter.lintAndFix(sourceFile, originalContent, effectiveConfig, (content, range) => { | ||
invalidatedProgram = true; | ||
const oldContent = sourceFile.text; | ||
sourceFile = ts.updateSourceFile(sourceFile, content, range); | ||
const hasErrors = utils_1.hasParseErrors(sourceFile); | ||
if (hasErrors) { | ||
log("Autofixing caused syntax errors in '%s', rolling back", sourceFile.fileName); | ||
sourceFile = ts.updateSourceFile(sourceFile, oldContent, utils_1.invertChangeRange(range)); | ||
} | ||
// either way we need to store the new SourceFile as the old one is now corrupted | ||
processorHost.updateSourceFile(sourceFile); | ||
return hasErrors ? undefined : sourceFile; | ||
}, fix === true ? undefined : fix, factory, mapped === undefined ? undefined : mapped.processor, linterOptions); | ||
} | ||
return program; | ||
}, | ||
}; | ||
for (const file of files) { | ||
else { | ||
summary = { | ||
findings: this.linter.getFindings(sourceFile, effectiveConfig, factory, mapped === undefined ? undefined : mapped.processor, linterOptions), | ||
fixes: 0, | ||
content: originalContent, | ||
}; | ||
} | ||
yield [originalName, summary]; | ||
} | ||
} | ||
} | ||
*lintFiles(options, config, linterOptions) { | ||
let processor; | ||
for (const file of getFiles(options.files, options.exclude, this.directories.getCurrentDirectory())) { | ||
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.reduce(config, originalName); | ||
const effectiveConfig = config && this.configManager.reduce(config, file); | ||
if (effectiveConfig === undefined) | ||
continue; | ||
let sourceFile = program.getSourceFile(file); | ||
const originalContent = mapped === undefined ? sourceFile.text : mapped.originalContent; | ||
let originalContent; | ||
let name; | ||
let content; | ||
if (effectiveConfig.processor) { | ||
const ctor = this.processorLoader.loadProcessor(effectiveConfig.processor); | ||
if (utils_1.hasSupportedExtension(file, options.extensions)) { | ||
name = file; | ||
} | ||
else { | ||
name = file + ctor.getSuffixForFile({ | ||
fileName: file, | ||
getSettings: () => effectiveConfig.settings, | ||
readFile: () => originalContent = this.fs.readFile(file), | ||
}); | ||
if (!utils_1.hasSupportedExtension(name, options.extensions)) | ||
continue; | ||
} | ||
if (originalContent === undefined) // might be initialized by the processor requesting the file content | ||
originalContent = this.fs.readFile(file); | ||
processor = new ctor({ | ||
source: originalContent, | ||
sourceFileName: file, | ||
targetFileName: name, | ||
settings: effectiveConfig.settings, | ||
}); | ||
content = processor.preprocess(); | ||
} | ||
else if (utils_1.hasSupportedExtension(file, options.extensions)) { | ||
processor = undefined; | ||
name = file; | ||
content = originalContent = this.fs.readFile(file); | ||
} | ||
else { | ||
continue; | ||
} | ||
let sourceFile = ts.createSourceFile(name, content, ts.ScriptTarget.ESNext, true); | ||
const fix = shouldFix(sourceFile, options, file); | ||
let summary; | ||
const fix = shouldFix(sourceFile, options, originalName); | ||
if (fix) { | ||
summary = this.linter.lintAndFix(sourceFile, originalContent, effectiveConfig, (content, range) => { | ||
invalidatedProgram = true; | ||
const oldContent = sourceFile.text; | ||
sourceFile = ts.updateSourceFile(sourceFile, content, range); | ||
const hasErrors = utils_1.hasParseErrors(sourceFile); | ||
if (hasErrors) { | ||
summary = this.linter.lintAndFix(sourceFile, originalContent, effectiveConfig, (newContent, range) => { | ||
sourceFile = ts.updateSourceFile(sourceFile, newContent, range); | ||
if (utils_1.hasParseErrors(sourceFile)) { | ||
log("Autofixing caused syntax errors in '%s', rolling back", sourceFile.fileName); | ||
sourceFile = ts.updateSourceFile(sourceFile, oldContent, utils_1.invertChangeRange(range)); | ||
// Note: 'sourceFile' shouldn't be used after this as it contains invalid code | ||
return; | ||
} | ||
// either way we need to store the new SourceFile as the old one is now corrupted | ||
processorHost.updateSourceFile(sourceFile); | ||
return hasErrors ? undefined : sourceFile; | ||
}, fix === true ? undefined : fix, factory, mapped === undefined ? undefined : mapped.processor, linterOptions); | ||
return sourceFile; | ||
}, fix === true ? undefined : fix, undefined, processor, linterOptions); | ||
} | ||
else { | ||
summary = { | ||
findings: this.linter.getFindings(sourceFile, effectiveConfig, factory, mapped === undefined ? undefined : mapped.processor, linterOptions), | ||
findings: this.linter.getFindings(sourceFile, effectiveConfig, undefined, processor, linterOptions), | ||
fixes: 0, | ||
@@ -97,203 +167,137 @@ content: originalContent, | ||
} | ||
yield [originalName, summary]; | ||
yield [file, summary]; | ||
} | ||
} | ||
} | ||
*lintFiles(options, config, linterOptions) { | ||
let processor; | ||
for (const file of getFiles(options.files, options.exclude, this.directories.getCurrentDirectory())) { | ||
if (options.config === undefined) | ||
config = this.configManager.find(file); | ||
const effectiveConfig = config && this.configManager.reduce(config, file); | ||
if (effectiveConfig === undefined) | ||
continue; | ||
let originalContent; | ||
let name; | ||
let content; | ||
if (effectiveConfig.processor) { | ||
const ctor = this.processorLoader.loadProcessor(effectiveConfig.processor); | ||
if (utils_1.hasSupportedExtension(file, options.extensions)) { | ||
name = file; | ||
} | ||
else { | ||
name = file + ctor.getSuffixForFile({ | ||
fileName: file, | ||
getSettings: () => effectiveConfig.settings, | ||
readFile: () => originalContent = this.fs.readFile(file), | ||
}); | ||
if (!utils_1.hasSupportedExtension(name, options.extensions)) | ||
continue; | ||
} | ||
if (originalContent === undefined) // might be initialized by the processor requesting the file content | ||
originalContent = this.fs.readFile(file); | ||
processor = new ctor({ | ||
source: originalContent, | ||
sourceFileName: file, | ||
targetFileName: name, | ||
settings: effectiveConfig.settings, | ||
}); | ||
content = processor.preprocess(); | ||
*getFilesAndProgram(projects, patterns, exclude, host, references) { | ||
const cwd = utils_1.unixifyPath(this.directories.getCurrentDirectory()); | ||
if (projects.length !== 0) { | ||
projects = projects.map((configFile) => this.checkConfigDirectory(utils_1.unixifyPath(path.resolve(cwd, configFile)))); | ||
} | ||
else if (utils_1.hasSupportedExtension(file, options.extensions)) { | ||
processor = undefined; | ||
name = file; | ||
content = originalContent = this.fs.readFile(file); | ||
else if (references) { | ||
projects = [this.checkConfigDirectory(cwd)]; | ||
} | ||
else { | ||
continue; | ||
const project = ts.findConfigFile(cwd, (f) => this.fs.isFile(f)); | ||
if (project === undefined) | ||
throw new ymir_1.ConfigurationError(`Cannot find tsconfig.json for directory '${cwd}'.`); | ||
projects = [project]; | ||
} | ||
let sourceFile = ts.createSourceFile(name, content, ts.ScriptTarget.ESNext, true); | ||
const fix = shouldFix(sourceFile, options, file); | ||
let summary; | ||
if (fix) { | ||
summary = this.linter.lintAndFix(sourceFile, originalContent, effectiveConfig, (newContent, range) => { | ||
sourceFile = ts.updateSourceFile(sourceFile, newContent, range); | ||
if (utils_1.hasParseErrors(sourceFile)) { | ||
log("Autofixing caused syntax errors in '%s', rolling back", sourceFile.fileName); | ||
// Note: 'sourceFile' shouldn't be used after this as it contains invalid code | ||
return; | ||
} | ||
return sourceFile; | ||
}, fix === true ? undefined : fix, undefined, processor, linterOptions); | ||
const allMatchedFiles = []; | ||
const include = []; | ||
const nonMagicGlobs = []; | ||
for (const pattern of patterns) { | ||
if (!pattern.hasMagic) { | ||
const mm = new minimatch_1.Minimatch(pattern.normalized[0]); | ||
nonMagicGlobs.push({ raw: pattern.normalized[0], match: mm }); | ||
include.push(mm); | ||
} | ||
else { | ||
include.push(...pattern.normalized.map((p) => new minimatch_1.Minimatch(p))); | ||
} | ||
} | ||
else { | ||
summary = { | ||
findings: this.linter.getFindings(sourceFile, effectiveConfig, undefined, processor, linterOptions), | ||
fixes: 0, | ||
content: originalContent, | ||
}; | ||
const ex = exclude.map((p) => new minimatch_1.Minimatch(p, { dot: true })); | ||
const projectsSeen = []; | ||
let filesOfPreviousProject; | ||
for (const program of this.createPrograms(projects, host, projectsSeen, references, isFileIncluded)) { | ||
const ownFiles = []; | ||
const files = []; | ||
const fileFilter = this.filterFactory.create({ program, host }); | ||
for (const sourceFile of program.getSourceFiles()) { | ||
if (!fileFilter.filter(sourceFile)) | ||
continue; | ||
const { fileName } = sourceFile; | ||
ownFiles.push(fileName); | ||
const originalName = host.getFileSystemFile(fileName); | ||
if (!isFileIncluded(originalName)) | ||
continue; | ||
files.push(fileName); | ||
allMatchedFiles.push(originalName); | ||
} | ||
// uncache all files of the previous project if they are no longer needed | ||
if (filesOfPreviousProject !== undefined) | ||
for (const oldFile of filesOfPreviousProject) | ||
if (!ownFiles.includes(oldFile)) | ||
host.uncacheFile(oldFile); | ||
filesOfPreviousProject = ownFiles; | ||
if (files.length !== 0) | ||
yield { files, program }; | ||
} | ||
yield [file, summary]; | ||
} | ||
} | ||
*getFilesAndProgram(projects, patterns, exclude, host, references) { | ||
const cwd = utils_1.unixifyPath(this.directories.getCurrentDirectory()); | ||
if (projects.length !== 0) { | ||
projects = projects.map((configFile) => this.checkConfigDirectory(utils_1.unixifyPath(path.resolve(cwd, configFile)))); | ||
} | ||
else if (references) { | ||
projects = [this.checkConfigDirectory(cwd)]; | ||
} | ||
else { | ||
const project = ts.findConfigFile(cwd, (f) => this.fs.isFile(f)); | ||
if (project === undefined) | ||
throw new ymir_1.ConfigurationError(`Cannot find tsconfig.json for directory '${cwd}'.`); | ||
projects = [project]; | ||
} | ||
const allMatchedFiles = []; | ||
const include = []; | ||
const nonMagicGlobs = []; | ||
for (const pattern of patterns) { | ||
if (!pattern.hasMagic) { | ||
const mm = new minimatch_1.Minimatch(pattern.normalized[0]); | ||
nonMagicGlobs.push({ raw: pattern.normalized[0], match: mm }); | ||
include.push(mm); | ||
ensurePatternsMatch(nonMagicGlobs, ex, allMatchedFiles, projectsSeen); | ||
function isFileIncluded(fileName) { | ||
return (include.length === 0 || include.some((p) => p.match(fileName))) && !ex.some((p) => p.match(fileName)); | ||
} | ||
else { | ||
include.push(...pattern.normalized.map((p) => new minimatch_1.Minimatch(p))); | ||
} | ||
} | ||
const ex = exclude.map((p) => new minimatch_1.Minimatch(p, { dot: true })); | ||
const projectsSeen = []; | ||
let filesOfPreviousProject; | ||
for (const program of this.createPrograms(projects, host, projectsSeen, references, isFileIncluded)) { | ||
const ownFiles = []; | ||
const files = []; | ||
const fileFilter = this.filterFactory.create({ program, host }); | ||
for (const sourceFile of program.getSourceFiles()) { | ||
if (!fileFilter.filter(sourceFile)) | ||
continue; | ||
const { fileName } = sourceFile; | ||
ownFiles.push(fileName); | ||
const originalName = host.getFileSystemFile(fileName); | ||
if (!isFileIncluded(originalName)) | ||
continue; | ||
files.push(fileName); | ||
allMatchedFiles.push(originalName); | ||
checkConfigDirectory(fileOrDirName) { | ||
switch (this.fs.getKind(fileOrDirName)) { | ||
case 0 /* NonExistent */: | ||
throw new ymir_1.ConfigurationError(`The specified path does not exist: '${fileOrDirName}'`); | ||
case 2 /* Directory */: { | ||
const file = utils_1.unixifyPath(path.join(fileOrDirName, 'tsconfig.json')); | ||
if (!this.fs.isFile(file)) | ||
throw new ymir_1.ConfigurationError(`Cannot find a tsconfig.json file at the specified directory: '${fileOrDirName}'`); | ||
return file; | ||
} | ||
default: | ||
return fileOrDirName; | ||
} | ||
// uncache all files of the previous project if they are no longer needed | ||
if (filesOfPreviousProject !== undefined) | ||
for (const oldFile of filesOfPreviousProject) | ||
if (!ownFiles.includes(oldFile)) | ||
host.uncacheFile(oldFile); | ||
filesOfPreviousProject = ownFiles; | ||
if (files.length !== 0) | ||
yield { files, program }; | ||
} | ||
ensurePatternsMatch(nonMagicGlobs, ex, allMatchedFiles, projectsSeen); | ||
function isFileIncluded(fileName) { | ||
return (include.length === 0 || include.some((p) => p.match(fileName))) && !ex.some((p) => p.match(fileName)); | ||
} | ||
} | ||
checkConfigDirectory(fileOrDirName) { | ||
switch (this.fs.getKind(fileOrDirName)) { | ||
case 0 /* NonExistent */: | ||
throw new ymir_1.ConfigurationError(`The specified path does not exist: '${fileOrDirName}'`); | ||
case 2 /* Directory */: { | ||
const file = utils_1.unixifyPath(path.join(fileOrDirName, 'tsconfig.json')); | ||
if (!this.fs.isFile(file)) | ||
throw new ymir_1.ConfigurationError(`Cannot find a tsconfig.json file at the specified directory: '${fileOrDirName}'`); | ||
return file; | ||
} | ||
default: | ||
return fileOrDirName; | ||
} | ||
} | ||
*createPrograms(projects, host, seen, references, isFileIncluded) { | ||
for (const configFile of projects) { | ||
if (configFile === undefined) | ||
continue; | ||
const configFilePath = typeof configFile === 'string' ? configFile : configFile.sourceFile.fileName; | ||
if (!utils_1.addUnique(seen, configFilePath)) | ||
continue; | ||
let commandLine; | ||
if (typeof configFile !== 'string') { | ||
({ commandLine } = configFile); | ||
} | ||
else { | ||
commandLine = host.getParsedCommandLine(configFile); | ||
if (commandLine === undefined) | ||
*createPrograms(projects, host, seen, references, isFileIncluded) { | ||
for (const configFile of projects) { | ||
if (configFile === undefined) | ||
continue; | ||
} | ||
if (commandLine.errors.length !== 0) | ||
this.logger.warn(ts.formatDiagnostics(commandLine.errors, host)); | ||
if (commandLine.fileNames.length !== 0) { | ||
if (!commandLine.options.composite || commandLine.fileNames.some((file) => isFileIncluded(host.getFileSystemFile(file)))) { | ||
log("Using project '%s'", configFilePath); | ||
let resolvedReferences; | ||
{ | ||
// this is in a nested block to allow garbage collection while recursing | ||
const program = host.createProgram(commandLine.fileNames, commandLine.options, undefined, commandLine.projectReferences); | ||
yield program; | ||
if (references) | ||
resolvedReferences = program.getResolvedProjectReferences(); | ||
} | ||
if (resolvedReferences !== undefined) | ||
yield* this.createPrograms(resolvedReferences, host, seen, true, isFileIncluded); | ||
const configFilePath = typeof configFile === 'string' ? configFile : configFile.sourceFile.fileName; | ||
if (!utils_1.addUnique(seen, configFilePath)) | ||
continue; | ||
} | ||
log("Project '%s' contains no file to lint", configFilePath); | ||
} | ||
if (references) { | ||
let commandLine; | ||
if (typeof configFile !== 'string') { | ||
if (configFile.references !== undefined) | ||
yield* this.createPrograms(configFile.references, host, seen, true, isFileIncluded); | ||
({ commandLine } = configFile); | ||
} | ||
else if (commandLine.projectReferences !== undefined) { | ||
yield* this.createPrograms(commandLine.projectReferences.map((ref) => this.checkConfigDirectory(ref.path)), host, seen, true, isFileIncluded); | ||
else { | ||
commandLine = host.getParsedCommandLine(configFile); | ||
if (commandLine === undefined) | ||
continue; | ||
} | ||
if (commandLine.errors.length !== 0) | ||
this.logger.warn(ts.formatDiagnostics(commandLine.errors, host)); | ||
if (commandLine.fileNames.length !== 0) { | ||
if (!commandLine.options.composite || commandLine.fileNames.some((file) => isFileIncluded(host.getFileSystemFile(file)))) { | ||
log("Using project '%s'", configFilePath); | ||
let resolvedReferences; | ||
{ | ||
// this is in a nested block to allow garbage collection while recursing | ||
const program = host.createProgram(commandLine.fileNames, commandLine.options, undefined, commandLine.projectReferences); | ||
yield program; | ||
if (references) | ||
resolvedReferences = program.getResolvedProjectReferences(); | ||
} | ||
if (resolvedReferences !== undefined) | ||
yield* this.createPrograms(resolvedReferences, host, seen, true, isFileIncluded); | ||
continue; | ||
} | ||
log("Project '%s' contains no file to lint", configFilePath); | ||
} | ||
if (references) { | ||
if (typeof configFile !== 'string') { | ||
if (configFile.references !== undefined) | ||
yield* this.createPrograms(configFile.references, host, seen, true, isFileIncluded); | ||
} | ||
else if (commandLine.projectReferences !== undefined) { | ||
yield* this.createPrograms(commandLine.projectReferences.map((ref) => this.checkConfigDirectory(ref.path)), host, seen, true, isFileIncluded); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
Runner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [cached_file_system_1.CachedFileSystem, | ||
configuration_manager_1.ConfigurationManager, | ||
linter_1.Linter, | ||
processor_loader_1.ProcessorLoader, | ||
ymir_1.DirectoryService, | ||
ymir_1.MessageHandler, | ||
ymir_1.FileFilterFactory]) | ||
], Runner); | ||
}; | ||
Runner = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [cached_file_system_1.CachedFileSystem, | ||
configuration_manager_1.ConfigurationManager, | ||
linter_1.Linter, | ||
processor_loader_1.ProcessorLoader, | ||
ymir_1.DirectoryService, | ||
ymir_1.MessageHandler, | ||
ymir_1.FileFilterFactory]) | ||
], Runner); | ||
return Runner; | ||
})(); | ||
exports.Runner = Runner; | ||
@@ -300,0 +304,0 @@ function getFiles(patterns, exclude, cwd) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.CachedFileSystem = exports.FileKind = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -16,118 +17,121 @@ const inversify_1 = require("inversify"); | ||
})(FileKind = exports.FileKind || (exports.FileKind = {})); | ||
let CachedFileSystem = class CachedFileSystem { | ||
constructor(fs, cache) { | ||
this.fs = fs; | ||
this.realpath = this.fs.realpath === undefined ? undefined : (file) => { | ||
return utils_1.resolveCachedResult(this.realpathCache, this.fs.normalizePath(file), () => this.fs.realpath(file)); | ||
}; | ||
this.fileKindCache = cache.create(); | ||
this.realpathCache = cache.create(); | ||
this.direntCache = cache.create(); | ||
} | ||
isFile(file) { | ||
return utils_1.resolveCachedResult(this.fileKindCache, this.fs.normalizePath(file), this.doGetKind) === 1 /* File */; | ||
} | ||
isDirectory(dir) { | ||
return utils_1.resolveCachedResult(this.fileKindCache, this.fs.normalizePath(dir), this.doGetKind) === 2 /* Directory */; | ||
} | ||
getKind(file) { | ||
return utils_1.resolveCachedResult(this.fileKindCache, this.fs.normalizePath(file), this.doGetKind); | ||
} | ||
doGetKind(file) { | ||
try { | ||
return statsToKind(this.fs.stat(file)); | ||
let CachedFileSystem = /** @class */ (() => { | ||
let CachedFileSystem = class CachedFileSystem { | ||
constructor(fs, cache) { | ||
this.fs = fs; | ||
this.realpath = this.fs.realpath === undefined ? undefined : (file) => { | ||
return utils_1.resolveCachedResult(this.realpathCache, this.fs.normalizePath(file), () => this.fs.realpath(file)); | ||
}; | ||
this.fileKindCache = cache.create(); | ||
this.realpathCache = cache.create(); | ||
this.direntCache = cache.create(); | ||
} | ||
catch { | ||
return 0 /* NonExistent */; | ||
isFile(file) { | ||
return utils_1.resolveCachedResult(this.fileKindCache, this.fs.normalizePath(file), this.doGetKind) === 1 /* File */; | ||
} | ||
} | ||
readDirectory(dir) { | ||
dir = this.fs.normalizePath(dir); | ||
let cachedResult = this.direntCache.get(dir); | ||
if (cachedResult !== undefined) | ||
return cachedResult.map((entry) => ({ kind: this.fileKindCache.get(this.fs.normalizePath(path.join(dir, entry))), name: entry })); | ||
cachedResult = []; | ||
const result = []; | ||
for (const entry of this.fs.readDirectory(dir)) { | ||
if (typeof entry === 'string') { | ||
cachedResult.push(entry); | ||
result.push({ kind: this.getKind(path.join(dir, entry)), name: entry }); | ||
isDirectory(dir) { | ||
return utils_1.resolveCachedResult(this.fileKindCache, this.fs.normalizePath(dir), this.doGetKind) === 2 /* Directory */; | ||
} | ||
getKind(file) { | ||
return utils_1.resolveCachedResult(this.fileKindCache, this.fs.normalizePath(file), this.doGetKind); | ||
} | ||
doGetKind(file) { | ||
try { | ||
return statsToKind(this.fs.stat(file)); | ||
} | ||
else { | ||
cachedResult.push(entry.name); | ||
const filePath = path.join(dir, entry.name); | ||
let kind; | ||
if (entry.isSymbolicLink()) { | ||
kind = this.getKind(filePath); | ||
catch { | ||
return 0 /* NonExistent */; | ||
} | ||
} | ||
readDirectory(dir) { | ||
dir = this.fs.normalizePath(dir); | ||
let cachedResult = this.direntCache.get(dir); | ||
if (cachedResult !== undefined) | ||
return cachedResult.map((entry) => ({ kind: this.fileKindCache.get(this.fs.normalizePath(path.join(dir, entry))), name: entry })); | ||
cachedResult = []; | ||
const result = []; | ||
for (const entry of this.fs.readDirectory(dir)) { | ||
if (typeof entry === 'string') { | ||
cachedResult.push(entry); | ||
result.push({ kind: this.getKind(path.join(dir, entry)), name: entry }); | ||
} | ||
else { | ||
kind = statsToKind(entry); | ||
this.fileKindCache.set(this.fs.normalizePath(filePath), kind); | ||
cachedResult.push(entry.name); | ||
const filePath = path.join(dir, entry.name); | ||
let kind; | ||
if (entry.isSymbolicLink()) { | ||
kind = this.getKind(filePath); | ||
} | ||
else { | ||
kind = statsToKind(entry); | ||
this.fileKindCache.set(this.fs.normalizePath(filePath), kind); | ||
} | ||
result.push({ kind, name: entry.name }); | ||
} | ||
result.push({ kind, name: entry.name }); | ||
} | ||
this.direntCache.set(dir, cachedResult); | ||
return result; | ||
} | ||
this.direntCache.set(dir, cachedResult); | ||
return result; | ||
} | ||
readFile(file) { | ||
return this.fs.readFile(this.fs.normalizePath(file)); | ||
} | ||
writeFile(file, content) { | ||
file = this.fs.normalizePath(file); | ||
this.fs.writeFile(file, content); | ||
this.updateCache(file, 1 /* File */); | ||
} | ||
remove(file) { | ||
file = this.fs.normalizePath(file); | ||
this.fs.deleteFile(file); | ||
this.updateCache(file, 0 /* NonExistent */); | ||
} | ||
createDirectory(dir) { | ||
dir = this.fs.normalizePath(dir); | ||
if (this.fileKindCache.get(dir) === 2 /* Directory */) | ||
return; | ||
return this.doCreateDirectory(dir); | ||
} | ||
doCreateDirectory(dir) { | ||
try { | ||
this.fs.createDirectory(dir); | ||
readFile(file) { | ||
return this.fs.readFile(this.fs.normalizePath(file)); | ||
} | ||
catch (e) { | ||
writeFile(file, content) { | ||
file = this.fs.normalizePath(file); | ||
this.fs.writeFile(file, content); | ||
this.updateCache(file, 1 /* File */); | ||
} | ||
remove(file) { | ||
file = this.fs.normalizePath(file); | ||
this.fs.deleteFile(file); | ||
this.updateCache(file, 0 /* NonExistent */); | ||
} | ||
createDirectory(dir) { | ||
dir = this.fs.normalizePath(dir); | ||
if (this.fileKindCache.get(dir) === 2 /* Directory */) | ||
return; | ||
return this.doCreateDirectory(dir); | ||
} | ||
doCreateDirectory(dir) { | ||
try { | ||
const stat = this.fs.stat(dir); | ||
if (!stat.isDirectory()) | ||
throw e; | ||
} | ||
catch { | ||
const parent = this.fs.normalizePath(path.dirname(dir)); | ||
if (parent === dir) | ||
throw e; | ||
this.doCreateDirectory(parent); | ||
this.fs.createDirectory(dir); | ||
} | ||
catch (e) { | ||
try { | ||
const stat = this.fs.stat(dir); | ||
if (!stat.isDirectory()) | ||
throw e; | ||
} | ||
catch { | ||
const parent = this.fs.normalizePath(path.dirname(dir)); | ||
if (parent === dir) | ||
throw e; | ||
this.doCreateDirectory(parent); | ||
this.fs.createDirectory(dir); | ||
} | ||
} | ||
this.updateCache(dir, 2 /* Directory */); | ||
} | ||
this.updateCache(dir, 2 /* Directory */); | ||
} | ||
updateCache(file, kind) { | ||
// this currently doesn't handle directory removal as there is no API for that | ||
if (this.fileKindCache.get(file) === kind) | ||
return; | ||
this.fileKindCache.set(file, kind); | ||
if (kind === 0 /* NonExistent */) | ||
this.realpathCache.delete(file); // only invalidate realpath cache on file removal | ||
// invalidate direntCache unconditionally as the new file's name might differ in case from the one we have here | ||
this.direntCache.delete(this.fs.normalizePath(path.dirname(file))); | ||
} | ||
}; | ||
tslib_1.__decorate([ | ||
bind_decorator_1.default, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", Number) | ||
], CachedFileSystem.prototype, "doGetKind", null); | ||
CachedFileSystem = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.FileSystem, ymir_1.CacheFactory]) | ||
], CachedFileSystem); | ||
updateCache(file, kind) { | ||
// this currently doesn't handle directory removal as there is no API for that | ||
if (this.fileKindCache.get(file) === kind) | ||
return; | ||
this.fileKindCache.set(file, kind); | ||
if (kind === 0 /* NonExistent */) | ||
this.realpathCache.delete(file); // only invalidate realpath cache on file removal | ||
// invalidate direntCache unconditionally as the new file's name might differ in case from the one we have here | ||
this.direntCache.delete(this.fs.normalizePath(path.dirname(file))); | ||
} | ||
}; | ||
tslib_1.__decorate([ | ||
bind_decorator_1.default, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", Number) | ||
], CachedFileSystem.prototype, "doGetKind", null); | ||
CachedFileSystem = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.FileSystem, ymir_1.CacheFactory]) | ||
], CachedFileSystem); | ||
return CachedFileSystem; | ||
})(); | ||
exports.CachedFileSystem = CachedFileSystem; | ||
@@ -134,0 +138,0 @@ function statsToKind(stats) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ConfigurationManager = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -11,91 +12,94 @@ const inversify_1 = require("inversify"); | ||
const cached_file_system_1 = require("./cached-file-system"); | ||
let ConfigurationManager = class ConfigurationManager { | ||
constructor(directories, configProvider, fs, cache) { | ||
this.directories = directories; | ||
this.configProvider = configProvider; | ||
this.fs = fs; | ||
this.configCache = cache.create(); | ||
} | ||
/** Look up the location of the configuration file for the specified file name. */ | ||
findPath(file) { | ||
file = path.resolve(this.directories.getCurrentDirectory(), file); | ||
try { | ||
return this.configProvider.find(file); | ||
let ConfigurationManager = /** @class */ (() => { | ||
let ConfigurationManager = class ConfigurationManager { | ||
constructor(directories, configProvider, fs, cache) { | ||
this.directories = directories; | ||
this.configProvider = configProvider; | ||
this.fs = fs; | ||
this.configCache = cache.create(); | ||
} | ||
catch (e) { | ||
throw new ymir_1.ConfigurationError(`Error finding configuration for '${file}': ${e && e.message}`); | ||
/** Look up the location of the configuration file for the specified file name. */ | ||
findPath(file) { | ||
file = path.resolve(this.directories.getCurrentDirectory(), file); | ||
try { | ||
return this.configProvider.find(file); | ||
} | ||
catch (e) { | ||
throw new ymir_1.ConfigurationError(`Error finding configuration for '${file}': ${e && e.message}`); | ||
} | ||
} | ||
} | ||
/** Load the configuration for the specified file. */ | ||
find(file) { | ||
const config = this.findPath(file); | ||
return config === undefined ? undefined : this.load(config); | ||
} | ||
/** Load the given config from a local file if it exists or from the resolved path otherwise */ | ||
loadLocalOrResolved(pathOrName, basedir = this.directories.getCurrentDirectory()) { | ||
const absolute = path.resolve(basedir, pathOrName); | ||
return this.load(this.fs.isFile(absolute) ? absolute : this.resolve(pathOrName, basedir)); | ||
} | ||
/** | ||
* Resolve a configuration name to it's absolute path. | ||
* | ||
* @param name | ||
* - name of a builtin config | ||
* - package name in `node_modules` or a submodule thereof | ||
* - absolute path | ||
* - relative path starting with `./` or `../` | ||
*/ | ||
resolve(name, basedir) { | ||
try { | ||
return this.configProvider.resolve(name, path.resolve(this.directories.getCurrentDirectory(), basedir)); | ||
/** Load the configuration for the specified file. */ | ||
find(file) { | ||
const config = this.findPath(file); | ||
return config === undefined ? undefined : this.load(config); | ||
} | ||
catch (e) { | ||
throw new ymir_1.ConfigurationError(`${e && e.message}`); | ||
/** Load the given config from a local file if it exists or from the resolved path otherwise */ | ||
loadLocalOrResolved(pathOrName, basedir = this.directories.getCurrentDirectory()) { | ||
const absolute = path.resolve(basedir, pathOrName); | ||
return this.load(this.fs.isFile(absolute) ? absolute : this.resolve(pathOrName, basedir)); | ||
} | ||
} | ||
/** | ||
* Collects all matching configuration options for the given file. Flattens all base configs and matches overrides. | ||
* Returns `undefined` if the file is excluded in one of the configuraton files. | ||
*/ | ||
reduce(config, file) { | ||
return reduceConfig(config, path.resolve(this.directories.getCurrentDirectory(), file), { rules: new Map(), settings: new Map(), processor: undefined }); | ||
} | ||
/** Get the processor configuration for a given file. */ | ||
getProcessor(config, fileName) { | ||
return findProcessorInConfig(config, path.resolve(this.directories.getCurrentDirectory(), fileName)) || undefined; | ||
} | ||
/** Get the settings for a given file. */ | ||
getSettings(config, fileName) { | ||
return reduceSettings(config, path.resolve(this.directories.getCurrentDirectory(), fileName), new Map()); | ||
} | ||
/** Load a configuration from a resolved path using the ConfigurationProvider, recursively resolving and loading base configs. */ | ||
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; | ||
}; | ||
try { | ||
return utils_1.resolveCachedResult(this.configCache, path.resolve(this.directories.getCurrentDirectory(), fileName), loadResolved); | ||
/** | ||
* Resolve a configuration name to it's absolute path. | ||
* | ||
* @param name | ||
* - name of a builtin config | ||
* - package name in `node_modules` or a submodule thereof | ||
* - absolute path | ||
* - relative path starting with `./` or `../` | ||
*/ | ||
resolve(name, basedir) { | ||
try { | ||
return this.configProvider.resolve(name, path.resolve(this.directories.getCurrentDirectory(), basedir)); | ||
} | ||
catch (e) { | ||
throw new ymir_1.ConfigurationError(`${e && e.message}`); | ||
} | ||
} | ||
catch (e) { | ||
throw new ymir_1.ConfigurationError(`Error loading ${stack.join(' => ')}: ${e && e.message}`); | ||
/** | ||
* Collects all matching configuration options for the given file. Flattens all base configs and matches overrides. | ||
* Returns `undefined` if the file is excluded in one of the configuraton files. | ||
*/ | ||
reduce(config, file) { | ||
return reduceConfig(config, path.resolve(this.directories.getCurrentDirectory(), file), { rules: new Map(), settings: new Map(), processor: undefined }); | ||
} | ||
} | ||
}; | ||
ConfigurationManager = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.DirectoryService, | ||
ymir_1.ConfigurationProvider, | ||
cached_file_system_1.CachedFileSystem, | ||
ymir_1.CacheFactory]) | ||
], ConfigurationManager); | ||
/** Get the processor configuration for a given file. */ | ||
getProcessor(config, fileName) { | ||
return findProcessorInConfig(config, path.resolve(this.directories.getCurrentDirectory(), fileName)) || undefined; | ||
} | ||
/** Get the settings for a given file. */ | ||
getSettings(config, fileName) { | ||
return reduceSettings(config, path.resolve(this.directories.getCurrentDirectory(), fileName), new Map()); | ||
} | ||
/** Load a configuration from a resolved path using the ConfigurationProvider, recursively resolving and loading base configs. */ | ||
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; | ||
}; | ||
try { | ||
return utils_1.resolveCachedResult(this.configCache, path.resolve(this.directories.getCurrentDirectory(), fileName), loadResolved); | ||
} | ||
catch (e) { | ||
throw new ymir_1.ConfigurationError(`Error loading ${stack.join(' => ')}: ${e && e.message}`); | ||
} | ||
} | ||
}; | ||
ConfigurationManager = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.DirectoryService, | ||
ymir_1.ConfigurationProvider, | ||
cached_file_system_1.CachedFileSystem, | ||
ymir_1.CacheFactory]) | ||
], ConfigurationManager); | ||
return ConfigurationManager; | ||
})(); | ||
exports.ConfigurationManager = ConfigurationManager; | ||
@@ -102,0 +106,0 @@ function reduceConfig(config, filename, receiver) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DefaultBuiltinResolver = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -7,26 +8,29 @@ const inversify_1 = require("inversify"); | ||
const path = require("path"); | ||
let DefaultBuiltinResolver = class DefaultBuiltinResolver { | ||
constructor(resolver) { | ||
this.resolver = resolver; | ||
} | ||
get builtinPackagePath() { | ||
const resolved = path.dirname(this.resolver.resolve('@fimbul/mimir', path.join(__dirname, '../'.repeat(/*offset to package root*/ 3)), [])); | ||
Object.defineProperty(this, 'builtinPackagePath', { value: resolved }); | ||
return resolved; | ||
} | ||
resolveConfig(name) { | ||
return path.join(this.builtinPackagePath, name + '.yaml'); | ||
} | ||
resolveRule(name) { | ||
return path.join(this.builtinPackagePath, `src/rules/${name}.js`); | ||
} | ||
resolveFormatter(name) { | ||
return path.join(this.builtinPackagePath, `src/formatters/${name}.js`); | ||
} | ||
}; | ||
DefaultBuiltinResolver = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.Resolver]) | ||
], DefaultBuiltinResolver); | ||
let DefaultBuiltinResolver = /** @class */ (() => { | ||
let DefaultBuiltinResolver = class DefaultBuiltinResolver { | ||
constructor(resolver) { | ||
this.resolver = resolver; | ||
} | ||
get builtinPackagePath() { | ||
const resolved = path.dirname(this.resolver.resolve('@fimbul/mimir', path.join(__dirname, '../'.repeat(/*offset to package root*/ 3)), [])); | ||
Object.defineProperty(this, 'builtinPackagePath', { value: resolved }); | ||
return resolved; | ||
} | ||
resolveConfig(name) { | ||
return path.join(this.builtinPackagePath, name + '.yaml'); | ||
} | ||
resolveRule(name) { | ||
return path.join(this.builtinPackagePath, `src/rules/${name}.js`); | ||
} | ||
resolveFormatter(name) { | ||
return path.join(this.builtinPackagePath, `src/formatters/${name}.js`); | ||
} | ||
}; | ||
DefaultBuiltinResolver = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.Resolver]) | ||
], DefaultBuiltinResolver); | ||
return DefaultBuiltinResolver; | ||
})(); | ||
exports.DefaultBuiltinResolver = DefaultBuiltinResolver; | ||
//# sourceMappingURL=builtin-resolver.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DefaultCacheFactory = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -25,12 +26,15 @@ const inversify_1 = require("inversify"); | ||
} | ||
let DefaultCacheFactory = class DefaultCacheFactory { | ||
// wotan-enable no-misused-generics | ||
create(weak) { | ||
return weak ? new WeakCache() : new Map(); | ||
} | ||
}; | ||
DefaultCacheFactory = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], DefaultCacheFactory); | ||
let DefaultCacheFactory = /** @class */ (() => { | ||
let DefaultCacheFactory = class DefaultCacheFactory { | ||
// wotan-enable no-misused-generics | ||
create(weak) { | ||
return weak ? new WeakCache() : new Map(); | ||
} | ||
}; | ||
DefaultCacheFactory = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], DefaultCacheFactory); | ||
return DefaultCacheFactory; | ||
})(); | ||
exports.DefaultCacheFactory = DefaultCacheFactory; | ||
//# sourceMappingURL=cache-factory.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DefaultConfigurationProvider = exports.CONFIG_FILENAMES = exports.CONFIG_EXTENSIONS = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -14,110 +15,113 @@ const inversify_1 = require("inversify"); | ||
exports.CONFIG_FILENAMES = exports.CONFIG_EXTENSIONS.map((ext) => '.wotanrc' + ext); | ||
let DefaultConfigurationProvider = class DefaultConfigurationProvider { | ||
constructor(fs, resolver, builtinResolver, cache) { | ||
this.fs = fs; | ||
this.resolver = resolver; | ||
this.builtinResolver = builtinResolver; | ||
this.cache = cache.create(); | ||
} | ||
find(fileToLint) { | ||
return utils_1.resolveCachedResult(this.cache, path.dirname(fileToLint), this.findConfigForDirectory); | ||
} | ||
findConfigForDirectory(dir) { | ||
for (let name of exports.CONFIG_FILENAMES) { | ||
name = path.join(dir, name); | ||
if (this.fs.isFile(name)) | ||
return name; | ||
let DefaultConfigurationProvider = /** @class */ (() => { | ||
let DefaultConfigurationProvider = class DefaultConfigurationProvider { | ||
constructor(fs, resolver, builtinResolver, cache) { | ||
this.fs = fs; | ||
this.resolver = resolver; | ||
this.builtinResolver = builtinResolver; | ||
this.cache = cache.create(); | ||
} | ||
const parent = path.dirname(dir); | ||
return parent === dir ? undefined : utils_1.resolveCachedResult(this.cache, parent, this.findConfigForDirectory); | ||
} | ||
resolve(name, basedir) { | ||
if (name.startsWith('wotan:')) { | ||
const fileName = this.builtinResolver.resolveConfig(name.substr('wotan:'.length)); | ||
if (!this.fs.isFile(fileName)) | ||
throw new Error(`'${name}' is not a valid builtin configuration, try 'wotan:recommended'.`); | ||
return fileName; | ||
find(fileToLint) { | ||
return utils_1.resolveCachedResult(this.cache, path.dirname(fileToLint), this.findConfigForDirectory); | ||
} | ||
return this.resolver.resolve(name, basedir, exports.CONFIG_EXTENSIONS, module.paths.slice(utils_1.OFFSET_TO_NODE_MODULES + 2)); | ||
} | ||
load(filename, context) { | ||
return this.parse(this.read(filename), filename, context); | ||
} | ||
parse(raw, filename, context) { | ||
const dirname = path.dirname(filename); | ||
const baseConfigs = utils_1.arrayify(raw.extends).map((base) => context.load(base)); | ||
let rulesDirectories; | ||
let aliases; | ||
for (const base of baseConfigs) { | ||
if (base.rulesDirectories !== undefined) { | ||
if (rulesDirectories === undefined) { | ||
rulesDirectories = new Map(Array.from(base.rulesDirectories, (v) => [v[0], v[1].slice()])); | ||
} | ||
else { | ||
extendRulesDirectories(rulesDirectories, base.rulesDirectories); | ||
} | ||
findConfigForDirectory(dir) { | ||
for (let name of exports.CONFIG_FILENAMES) { | ||
name = path.join(dir, name); | ||
if (this.fs.isFile(name)) | ||
return name; | ||
} | ||
if (base.aliases !== undefined) { | ||
if (aliases === undefined) { | ||
aliases = new Map(base.aliases); | ||
const parent = path.dirname(dir); | ||
return parent === dir ? undefined : utils_1.resolveCachedResult(this.cache, parent, this.findConfigForDirectory); | ||
} | ||
resolve(name, basedir) { | ||
if (name.startsWith('wotan:')) { | ||
const fileName = this.builtinResolver.resolveConfig(name.substr('wotan:'.length)); | ||
if (!this.fs.isFile(fileName)) | ||
throw new Error(`'${name}' is not a valid builtin configuration, try 'wotan:recommended'.`); | ||
return fileName; | ||
} | ||
return this.resolver.resolve(name, basedir, exports.CONFIG_EXTENSIONS, module.paths.slice(utils_1.OFFSET_TO_NODE_MODULES + 2)); | ||
} | ||
load(filename, context) { | ||
return this.parse(this.read(filename), filename, context); | ||
} | ||
parse(raw, filename, context) { | ||
const dirname = path.dirname(filename); | ||
const baseConfigs = utils_1.arrayify(raw.extends).map((base) => context.load(base)); | ||
let rulesDirectories; | ||
let aliases; | ||
for (const base of baseConfigs) { | ||
if (base.rulesDirectories !== undefined) { | ||
if (rulesDirectories === undefined) { | ||
rulesDirectories = new Map(Array.from(base.rulesDirectories, (v) => [v[0], v[1].slice()])); | ||
} | ||
else { | ||
extendRulesDirectories(rulesDirectories, base.rulesDirectories); | ||
} | ||
} | ||
else { | ||
for (const [name, alias] of base.aliases) | ||
aliases.set(name, alias); | ||
if (base.aliases !== undefined) { | ||
if (aliases === undefined) { | ||
aliases = new Map(base.aliases); | ||
} | ||
else { | ||
for (const [name, alias] of base.aliases) | ||
aliases.set(name, alias); | ||
} | ||
} | ||
} | ||
if (raw.rulesDirectories) | ||
rulesDirectories = mapRulesDirectories(rulesDirectories, raw.rulesDirectories, dirname); | ||
if (raw.aliases) | ||
aliases = resolveAliases(raw.aliases, aliases, rulesDirectories); | ||
return { | ||
aliases, | ||
filename, | ||
rulesDirectories, | ||
extends: baseConfigs, | ||
overrides: raw.overrides && raw.overrides.map((o, i) => this.mapOverride(o, i, dirname, aliases, rulesDirectories)), | ||
rules: raw.rules ? mapRules(raw.rules, aliases, rulesDirectories) : undefined, | ||
processor: this.mapProcessor(raw.processor, dirname), | ||
exclude: Array.isArray(raw.exclude) ? raw.exclude : raw.exclude ? [raw.exclude] : undefined, | ||
settings: raw.settings ? mapSettings(raw.settings) : undefined, | ||
}; | ||
} | ||
if (raw.rulesDirectories) | ||
rulesDirectories = mapRulesDirectories(rulesDirectories, raw.rulesDirectories, dirname); | ||
if (raw.aliases) | ||
aliases = resolveAliases(raw.aliases, aliases, rulesDirectories); | ||
return { | ||
aliases, | ||
filename, | ||
rulesDirectories, | ||
extends: baseConfigs, | ||
overrides: raw.overrides && raw.overrides.map((o, i) => this.mapOverride(o, i, dirname, aliases, rulesDirectories)), | ||
rules: raw.rules ? mapRules(raw.rules, aliases, rulesDirectories) : undefined, | ||
processor: this.mapProcessor(raw.processor, dirname), | ||
exclude: Array.isArray(raw.exclude) ? raw.exclude : raw.exclude ? [raw.exclude] : undefined, | ||
settings: raw.settings ? mapSettings(raw.settings) : undefined, | ||
}; | ||
} | ||
read(filename) { | ||
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)); | ||
default: | ||
return this.resolver.require(filename, { cache: false }); | ||
read(filename) { | ||
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)); | ||
default: | ||
return this.resolver.require(filename, { cache: false }); | ||
} | ||
} | ||
} | ||
mapOverride(raw, index, basedir, aliases, rulesDirectoryMap) { | ||
const files = utils_1.arrayify(raw.files); | ||
if (files.length === 0) | ||
throw new Error(`Override ${index} does not specify files.`); | ||
return { | ||
files, | ||
rules: raw.rules ? mapRules(raw.rules, aliases, rulesDirectoryMap) : undefined, | ||
settings: raw.settings ? mapSettings(raw.settings) : undefined, | ||
processor: this.mapProcessor(raw.processor, basedir), | ||
}; | ||
} | ||
mapProcessor(processor, basedir) { | ||
return processor && this.resolver.resolve(processor, basedir, undefined, module.paths.slice(utils_1.OFFSET_TO_NODE_MODULES + 2)); | ||
} | ||
}; | ||
tslib_1.__decorate([ | ||
bind_decorator_1.default, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", Object) | ||
], DefaultConfigurationProvider.prototype, "findConfigForDirectory", null); | ||
DefaultConfigurationProvider = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [cached_file_system_1.CachedFileSystem, ymir_1.Resolver, ymir_1.BuiltinResolver, ymir_1.CacheFactory]) | ||
], DefaultConfigurationProvider); | ||
mapOverride(raw, index, basedir, aliases, rulesDirectoryMap) { | ||
const files = utils_1.arrayify(raw.files); | ||
if (files.length === 0) | ||
throw new Error(`Override ${index} does not specify files.`); | ||
return { | ||
files, | ||
rules: raw.rules ? mapRules(raw.rules, aliases, rulesDirectoryMap) : undefined, | ||
settings: raw.settings ? mapSettings(raw.settings) : undefined, | ||
processor: this.mapProcessor(raw.processor, basedir), | ||
}; | ||
} | ||
mapProcessor(processor, basedir) { | ||
return processor && this.resolver.resolve(processor, basedir, undefined, module.paths.slice(utils_1.OFFSET_TO_NODE_MODULES + 2)); | ||
} | ||
}; | ||
tslib_1.__decorate([ | ||
bind_decorator_1.default, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", Object) | ||
], DefaultConfigurationProvider.prototype, "findConfigForDirectory", null); | ||
DefaultConfigurationProvider = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [cached_file_system_1.CachedFileSystem, ymir_1.Resolver, ymir_1.BuiltinResolver, ymir_1.CacheFactory]) | ||
], DefaultConfigurationProvider); | ||
return DefaultConfigurationProvider; | ||
})(); | ||
exports.DefaultConfigurationProvider = DefaultConfigurationProvider; | ||
@@ -124,0 +128,0 @@ function mapRulesDirectories(receiver, raw, dirname) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DefaultDeprecationHandler = void 0; | ||
const tslib_1 = require("tslib"); | ||
const ymir_1 = require("@fimbul/ymir"); | ||
const inversify_1 = require("inversify"); | ||
let DefaultDeprecationHandler = class DefaultDeprecationHandler { | ||
constructor(logger) { | ||
this.logger = logger; | ||
} | ||
handle(target, name, text) { | ||
this.logger.warn(`${titlecase(target)} '${name}' is deprecated${text ? `: ${text}` : '.'}`); | ||
} | ||
}; | ||
DefaultDeprecationHandler = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.MessageHandler]) | ||
], DefaultDeprecationHandler); | ||
let DefaultDeprecationHandler = /** @class */ (() => { | ||
let DefaultDeprecationHandler = class DefaultDeprecationHandler { | ||
constructor(logger) { | ||
this.logger = logger; | ||
} | ||
handle(target, name, text) { | ||
this.logger.warn(`${titlecase(target)} '${name}' is deprecated${text ? `: ${text}` : '.'}`); | ||
} | ||
}; | ||
DefaultDeprecationHandler = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.MessageHandler]) | ||
], DefaultDeprecationHandler); | ||
return DefaultDeprecationHandler; | ||
})(); | ||
exports.DefaultDeprecationHandler = DefaultDeprecationHandler; | ||
@@ -19,0 +23,0 @@ function titlecase(str) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NodeDirectoryService = void 0; | ||
const tslib_1 = require("tslib"); | ||
const inversify_1 = require("inversify"); | ||
const os = require("os"); | ||
let NodeDirectoryService = class NodeDirectoryService { | ||
getCurrentDirectory() { | ||
return process.cwd(); | ||
} | ||
getHomeDirectory() { | ||
return os.homedir(); | ||
} | ||
}; | ||
NodeDirectoryService = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], NodeDirectoryService); | ||
let NodeDirectoryService = /** @class */ (() => { | ||
let NodeDirectoryService = class NodeDirectoryService { | ||
getCurrentDirectory() { | ||
return process.cwd(); | ||
} | ||
getHomeDirectory() { | ||
return os.homedir(); | ||
} | ||
}; | ||
NodeDirectoryService = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], NodeDirectoryService); | ||
return NodeDirectoryService; | ||
})(); | ||
exports.NodeDirectoryService = NodeDirectoryService; | ||
//# sourceMappingURL=directory-service.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DefaultFileFilterFactory = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -8,10 +9,13 @@ const inversify_1 = require("inversify"); | ||
const path = require("path"); | ||
let DefaultFileFilterFactory = class DefaultFileFilterFactory { | ||
create(context) { | ||
return new DefaultFileFilter(context.program, context.host); | ||
} | ||
}; | ||
DefaultFileFilterFactory = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], DefaultFileFilterFactory); | ||
let DefaultFileFilterFactory = /** @class */ (() => { | ||
let DefaultFileFilterFactory = class DefaultFileFilterFactory { | ||
create(context) { | ||
return new DefaultFileFilter(context.program, context.host); | ||
} | ||
}; | ||
DefaultFileFilterFactory = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], DefaultFileFilterFactory); | ||
return DefaultFileFilterFactory; | ||
})(); | ||
exports.DefaultFileFilterFactory = DefaultFileFilterFactory; | ||
@@ -18,0 +22,0 @@ class DefaultFileFilter { |
"use strict"; | ||
var NodeFileSystem_1; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NodeFileSystem = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -10,57 +10,63 @@ const ymir_1 = require("@fimbul/ymir"); | ||
const ts = require("typescript"); | ||
let NodeFileSystem = NodeFileSystem_1 = class NodeFileSystem { | ||
constructor(logger) { | ||
this.logger = logger; | ||
} | ||
static normalizePath(path) { | ||
return utils_1.unixifyPath(ts.sys.useCaseSensitiveFileNames ? path : path.toLowerCase()); | ||
} | ||
normalizePath(path) { | ||
return NodeFileSystem_1.normalizePath(path); | ||
} | ||
readFile(file) { | ||
const buf = fs.readFileSync(file); | ||
const len = buf.length; | ||
// detect MPEG TS files and treat them as empty | ||
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 ''; | ||
let NodeFileSystem = /** @class */ (() => { | ||
var NodeFileSystem_1; | ||
let NodeFileSystem = NodeFileSystem_1 = class NodeFileSystem { | ||
constructor(logger) { | ||
this.logger = logger; | ||
} | ||
if (len >= 2) { | ||
if (buf[0] === 0xFE && buf[1] === 0xFF) // UTF16BE BOM | ||
return buf.swap16().toString('utf16le', 2); | ||
if (buf[0] === 0xFF && buf[1] === 0xFE) // UTF16LE BOM | ||
return buf.toString('utf16le', 2); | ||
if (len >= 3 && buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF) // UTF8 with BOM | ||
return buf.toString('utf8'); | ||
static normalizePath(path) { | ||
const normalized = utils_1.unixifyPath(ts.sys.useCaseSensitiveFileNames ? path : path.toLowerCase()); | ||
// handle windows drive names 'C:' -> 'C:/', because reading 'C:' actually reads '.' | ||
return normalized.includes('/') ? normalized : normalized + '/'; | ||
} | ||
return buf.toString('utf8'); // default to UTF8 without BOM | ||
} | ||
readDirectory(dir) { | ||
return fs.readdirSync(dir, { withFileTypes: true }); | ||
} | ||
stat(path) { | ||
return fs.statSync(path); | ||
} | ||
realpath(path) { | ||
return fs.realpathSync(path); | ||
} | ||
writeFile(file, content) { | ||
return fs.writeFileSync(file, content); | ||
} | ||
deleteFile(path) { | ||
return fs.unlinkSync(path); | ||
} | ||
createDirectory(dir) { | ||
return fs.mkdirSync(dir, { recursive: true }); | ||
} | ||
}; | ||
NodeFileSystem = NodeFileSystem_1 = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.MessageHandler]) | ||
], NodeFileSystem); | ||
normalizePath(path) { | ||
return NodeFileSystem_1.normalizePath(path); | ||
} | ||
readFile(file) { | ||
const buf = fs.readFileSync(file); | ||
const len = buf.length; | ||
// detect MPEG TS files and treat them as empty | ||
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) { | ||
if (buf[0] === 0xFE && buf[1] === 0xFF) // UTF16BE BOM | ||
return buf.swap16().toString('utf16le', 2); | ||
if (buf[0] === 0xFF && buf[1] === 0xFE) // UTF16LE BOM | ||
return buf.toString('utf16le', 2); | ||
if (len >= 3 && buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF) // UTF8 with BOM | ||
return buf.toString('utf8'); | ||
} | ||
return buf.toString('utf8'); // default to UTF8 without BOM | ||
} | ||
readDirectory(dir) { | ||
return fs.readdirSync(dir, { withFileTypes: true }); | ||
} | ||
stat(path) { | ||
return fs.statSync(path); | ||
} | ||
realpath(path) { | ||
return fs.realpathSync(path); | ||
} | ||
writeFile(file, content) { | ||
return fs.writeFileSync(file, content); | ||
} | ||
deleteFile(path) { | ||
return fs.unlinkSync(path); | ||
} | ||
createDirectory(dir) { | ||
return fs.mkdirSync(dir, { recursive: true }); | ||
} | ||
}; | ||
NodeFileSystem = NodeFileSystem_1 = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.MessageHandler]) | ||
], NodeFileSystem); | ||
return NodeFileSystem; | ||
})(); | ||
exports.NodeFileSystem = NodeFileSystem; | ||
//# sourceMappingURL=file-system.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NodeFormatterLoader = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -7,32 +8,35 @@ const inversify_1 = require("inversify"); | ||
const utils_1 = require("../../utils"); | ||
let NodeFormatterLoader = class NodeFormatterLoader { | ||
constructor(resolver, builtinResolver) { | ||
this.resolver = resolver; | ||
this.builtinResolver = builtinResolver; | ||
} | ||
loadCoreFormatter(name) { | ||
name = this.builtinResolver.resolveFormatter(name); | ||
try { | ||
name = this.resolver.resolve(name); | ||
let NodeFormatterLoader = /** @class */ (() => { | ||
let NodeFormatterLoader = class NodeFormatterLoader { | ||
constructor(resolver, builtinResolver) { | ||
this.resolver = resolver; | ||
this.builtinResolver = builtinResolver; | ||
} | ||
catch { | ||
return; | ||
loadCoreFormatter(name) { | ||
name = this.builtinResolver.resolveFormatter(name); | ||
try { | ||
name = this.resolver.resolve(name); | ||
} | ||
catch { | ||
return; | ||
} | ||
return this.resolver.require(name).Formatter; | ||
} | ||
return this.resolver.require(name).Formatter; | ||
} | ||
loadCustomFormatter(name, basedir) { | ||
try { | ||
name = this.resolver.resolve(name, basedir, undefined, module.paths.slice(utils_1.OFFSET_TO_NODE_MODULES + 2)); | ||
loadCustomFormatter(name, basedir) { | ||
try { | ||
name = this.resolver.resolve(name, basedir, undefined, module.paths.slice(utils_1.OFFSET_TO_NODE_MODULES + 2)); | ||
} | ||
catch { | ||
return; | ||
} | ||
return this.resolver.require(name).Formatter; | ||
} | ||
catch { | ||
return; | ||
} | ||
return this.resolver.require(name).Formatter; | ||
} | ||
}; | ||
NodeFormatterLoader = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.Resolver, ymir_1.BuiltinResolver]) | ||
], NodeFormatterLoader); | ||
}; | ||
NodeFormatterLoader = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.Resolver, ymir_1.BuiltinResolver]) | ||
], NodeFormatterLoader); | ||
return NodeFormatterLoader; | ||
})(); | ||
exports.NodeFormatterLoader = NodeFormatterLoader; | ||
//# sourceMappingURL=formatter-loader-host.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DefaultLineSwitchParser = exports.LineSwitchFilterFactory = exports.LINE_SWITCH_REGEX = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -9,68 +10,70 @@ const inversify_1 = require("inversify"); | ||
exports.LINE_SWITCH_REGEX = /^ *wotan-(enable|disable)((?:-next)?-line)?( +(?:(?:[\w-]+\/)*[\w-]+ *, *)*(?:[\w-]+\/)*[\w-]+)? *$/; | ||
let LineSwitchFilterFactory = class LineSwitchFilterFactory { | ||
constructor(parser) { | ||
this.parser = parser; | ||
} | ||
create(context) { | ||
const { disables, switches } = this.parseLineSwitches(context); | ||
return new Filter(disables, switches, context.sourceFile); | ||
} | ||
getDisabledRanges(context) { | ||
// remove internal `switch` property from ranges | ||
return new Map(Array.from(this.parseLineSwitches(context).disables, (entry) => [ | ||
entry[0], | ||
entry[1].map((range) => ({ pos: range.pos, end: range.end })), | ||
])); | ||
} | ||
parseLineSwitches(context) { | ||
const { sourceFile, ruleNames } = context; | ||
let wrappedAst; | ||
const raw = this.parser.parse({ | ||
sourceFile, | ||
getCommentAtPosition(pos) { | ||
const wrap = tsutils_1.getWrappedNodeAtPosition(wrappedAst || (wrappedAst = context.getWrappedAst()), pos); | ||
if (wrap === undefined) | ||
return; | ||
return tsutils_1.getCommentAtPosition(sourceFile, pos, wrap.node); | ||
}, | ||
}); | ||
const lineSwitches = []; | ||
const result = new Map(); | ||
for (const rawLineSwitch of raw) { | ||
const lineSwitch = { | ||
location: rawLineSwitch.location, | ||
enable: rawLineSwitch.enable, | ||
rules: [], | ||
outOfRange: rawLineSwitch.end <= 0 || rawLineSwitch.pos > sourceFile.end, | ||
}; | ||
lineSwitches.push(lineSwitch); | ||
if (lineSwitch.outOfRange) | ||
continue; | ||
const rulesToSwitch = new Map(); | ||
for (const rawRuleSwitch of rawLineSwitch.rules) { | ||
const ruleSwitch = { | ||
location: rawRuleSwitch.location, | ||
fixLocation: rawRuleSwitch.fixLocation || rawRuleSwitch.location, | ||
state: 0 /* NoMatch */, | ||
let LineSwitchFilterFactory = /** @class */ (() => { | ||
let LineSwitchFilterFactory = class LineSwitchFilterFactory { | ||
constructor(parser) { | ||
this.parser = parser; | ||
} | ||
create(context) { | ||
const { disables, switches } = this.parseLineSwitches(context); | ||
return new Filter(disables, switches, context.sourceFile); | ||
} | ||
getDisabledRanges(context) { | ||
// remove internal `switch` property from ranges | ||
return new Map(Array.from(this.parseLineSwitches(context).disables, (entry) => [ | ||
entry[0], | ||
entry[1].map((range) => ({ pos: range.pos, end: range.end })), | ||
])); | ||
} | ||
parseLineSwitches(context) { | ||
const { sourceFile, ruleNames } = context; | ||
let wrappedAst; | ||
const raw = this.parser.parse({ | ||
sourceFile, | ||
getCommentAtPosition(pos) { | ||
const wrap = tsutils_1.getWrappedNodeAtPosition(wrappedAst || (wrappedAst = context.getWrappedAst()), pos); | ||
if (wrap === undefined) | ||
return; | ||
return tsutils_1.getCommentAtPosition(sourceFile, pos, wrap.node); | ||
}, | ||
}); | ||
const lineSwitches = []; | ||
const result = new Map(); | ||
for (const rawLineSwitch of raw) { | ||
const lineSwitch = { | ||
location: rawLineSwitch.location, | ||
enable: rawLineSwitch.enable, | ||
rules: [], | ||
outOfRange: rawLineSwitch.end <= 0 || rawLineSwitch.pos > sourceFile.end, | ||
}; | ||
lineSwitch.rules.push(ruleSwitch); | ||
if (typeof rawRuleSwitch.predicate === 'string') { | ||
if (ruleNames.includes(rawRuleSwitch.predicate)) { | ||
if (rulesToSwitch.has(rawRuleSwitch.predicate)) { | ||
ruleSwitch.state = 2 /* Redundant */; | ||
lineSwitches.push(lineSwitch); | ||
if (lineSwitch.outOfRange) | ||
continue; | ||
const rulesToSwitch = new Map(); | ||
for (const rawRuleSwitch of rawLineSwitch.rules) { | ||
const ruleSwitch = { | ||
location: rawRuleSwitch.location, | ||
fixLocation: rawRuleSwitch.fixLocation || rawRuleSwitch.location, | ||
state: 0 /* NoMatch */, | ||
}; | ||
lineSwitch.rules.push(ruleSwitch); | ||
if (typeof rawRuleSwitch.predicate === 'string') { | ||
if (ruleNames.includes(rawRuleSwitch.predicate)) { | ||
if (rulesToSwitch.has(rawRuleSwitch.predicate)) { | ||
ruleSwitch.state = 2 /* Redundant */; | ||
} | ||
else { | ||
rulesToSwitch.set(rawRuleSwitch.predicate, ruleSwitch); | ||
ruleSwitch.state = 1 /* NoChange */; | ||
} | ||
} | ||
else { | ||
rulesToSwitch.set(rawRuleSwitch.predicate, ruleSwitch); | ||
ruleSwitch.state = 1 /* NoChange */; | ||
} | ||
} | ||
} | ||
else { | ||
const matchingNames = ruleNames.filter(makeFilterPredicate(rawRuleSwitch.predicate)); | ||
if (matchingNames.length !== 0) { | ||
ruleSwitch.state = 2 /* Redundant */; | ||
for (const rule of matchingNames) { | ||
if (!rulesToSwitch.has(rule)) { | ||
rulesToSwitch.set(rule, ruleSwitch); | ||
ruleSwitch.state = 1 /* NoChange */; | ||
else { | ||
const matchingNames = ruleNames.filter(makeFilterPredicate(rawRuleSwitch.predicate)); | ||
if (matchingNames.length !== 0) { | ||
ruleSwitch.state = 2 /* Redundant */; | ||
for (const rule of matchingNames) { | ||
if (!rulesToSwitch.has(rule)) { | ||
rulesToSwitch.set(rule, ruleSwitch); | ||
ruleSwitch.state = 1 /* NoChange */; | ||
} | ||
} | ||
@@ -80,42 +83,43 @@ } | ||
} | ||
} | ||
for (const [rule, ruleSwitch] of rulesToSwitch) { | ||
const ranges = result.get(rule); | ||
if (ranges === undefined) { | ||
if (rawLineSwitch.enable) | ||
continue; // rule is already enabled | ||
result.set(rule, [{ pos: rawLineSwitch.pos, end: rawLineSwitch.end === undefined ? Infinity : rawLineSwitch.end, switch: ruleSwitch }]); | ||
} | ||
else { | ||
const last = ranges[ranges.length - 1]; | ||
if (last.end === Infinity) { | ||
if (!rawLineSwitch.enable) | ||
continue; // rule is already disabled | ||
last.end = rawLineSwitch.pos; | ||
if (rawLineSwitch.end !== undefined) | ||
ranges.push({ pos: rawLineSwitch.end, end: Infinity, switch: ruleSwitch }); | ||
for (const [rule, ruleSwitch] of rulesToSwitch) { | ||
const ranges = result.get(rule); | ||
if (ranges === undefined) { | ||
if (rawLineSwitch.enable) | ||
continue; // rule is already enabled | ||
result.set(rule, [{ pos: rawLineSwitch.pos, end: rawLineSwitch.end === undefined ? Infinity : rawLineSwitch.end, switch: ruleSwitch }]); | ||
} | ||
else if (rawLineSwitch.enable || rawLineSwitch.pos < last.end) { | ||
// rule is already enabled | ||
// or disabled range is nested inside the previous range | ||
continue; | ||
} | ||
else { | ||
ranges.push({ | ||
pos: rawLineSwitch.pos, | ||
end: rawLineSwitch.end === undefined ? Infinity : rawLineSwitch.end, | ||
switch: ruleSwitch, | ||
}); | ||
const last = ranges[ranges.length - 1]; | ||
if (last.end === Infinity) { | ||
if (!rawLineSwitch.enable) | ||
continue; // rule is already disabled | ||
last.end = rawLineSwitch.pos; | ||
if (rawLineSwitch.end !== undefined) | ||
ranges.push({ pos: rawLineSwitch.end, end: Infinity, switch: ruleSwitch }); | ||
} | ||
else if (rawLineSwitch.enable || rawLineSwitch.pos < last.end) { | ||
// rule is already enabled | ||
// or disabled range is nested inside the previous range | ||
continue; | ||
} | ||
else { | ||
ranges.push({ | ||
pos: rawLineSwitch.pos, | ||
end: rawLineSwitch.end === undefined ? Infinity : rawLineSwitch.end, | ||
switch: ruleSwitch, | ||
}); | ||
} | ||
} | ||
ruleSwitch.state = 3 /* Unused */; | ||
} | ||
ruleSwitch.state = 3 /* Unused */; | ||
} | ||
return { switches: lineSwitches, disables: result }; | ||
} | ||
return { switches: lineSwitches, disables: result }; | ||
} | ||
}; | ||
LineSwitchFilterFactory = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.LineSwitchParser]) | ||
], LineSwitchFilterFactory); | ||
}; | ||
LineSwitchFilterFactory = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.LineSwitchParser]) | ||
], LineSwitchFilterFactory); | ||
return LineSwitchFilterFactory; | ||
})(); | ||
exports.LineSwitchFilterFactory = LineSwitchFilterFactory; | ||
@@ -204,44 +208,21 @@ const stateText = { | ||
} | ||
let DefaultLineSwitchParser = class DefaultLineSwitchParser { | ||
parse(context) { | ||
const { sourceFile } = context; | ||
const result = []; | ||
const commentRegex = /(\/[/*] *wotan-(enable|disable)((?:-next)?-line)?)( +(?:(?:[\w-]+\/)*[\w-]+ *, *)*(?:[\w-]+\/)*[\w-]+)? *(?:$|\*\/)/mg; | ||
for (let match = commentRegex.exec(sourceFile.text); match !== null; match = commentRegex.exec(sourceFile.text)) { | ||
const comment = context.getCommentAtPosition(match.index); | ||
if (comment === undefined || comment.pos !== match.index || comment.end !== match.index + match[0].length) | ||
continue; | ||
const rules = match[4] === undefined ? [{ predicate: /^/ }] : parseRules(match[4], match.index + match[1].length); | ||
const enable = match[2] === 'enable'; | ||
switch (match[3]) { | ||
case '-line': { | ||
const lineStarts = sourceFile.getLineStarts(); | ||
const { line } = ts.getLineAndCharacterOfPosition(sourceFile, comment.pos); | ||
result.push({ | ||
rules, | ||
enable, | ||
pos: lineStarts[line], | ||
// no need to switch back if there is no next line | ||
end: lineStarts.length === line + 1 ? undefined : lineStarts[line + 1], | ||
location: { pos: comment.pos, end: comment.end }, | ||
}); | ||
break; | ||
} | ||
case '-next-line': { | ||
const lineStarts = sourceFile.getLineStarts(); | ||
const line = ts.getLineAndCharacterOfPosition(sourceFile, comment.pos).line + 1; | ||
if (lineStarts.length === line) { | ||
// there is no next line, return an out-of-range switch that can be reported | ||
let DefaultLineSwitchParser = /** @class */ (() => { | ||
let DefaultLineSwitchParser = class DefaultLineSwitchParser { | ||
parse(context) { | ||
const { sourceFile } = context; | ||
const result = []; | ||
const commentRegex = /(\/[/*] *wotan-(enable|disable)((?:-next)?-line)?)( +(?:(?:[\w-]+\/)*[\w-]+ *, *)*(?:[\w-]+\/)*[\w-]+)? *(?:$|\*\/)/mg; | ||
for (let match = commentRegex.exec(sourceFile.text); match !== null; match = commentRegex.exec(sourceFile.text)) { | ||
const comment = context.getCommentAtPosition(match.index); | ||
if (comment === undefined || comment.pos !== match.index || comment.end !== match.index + match[0].length) | ||
continue; | ||
const rules = match[4] === undefined ? [{ predicate: /^/ }] : parseRules(match[4], match.index + match[1].length); | ||
const enable = match[2] === 'enable'; | ||
switch (match[3]) { | ||
case '-line': { | ||
const lineStarts = sourceFile.getLineStarts(); | ||
const { line } = ts.getLineAndCharacterOfPosition(sourceFile, comment.pos); | ||
result.push({ | ||
rules, | ||
enable, | ||
pos: sourceFile.end + 1, | ||
end: undefined, | ||
location: { pos: comment.pos, end: comment.end }, | ||
}); | ||
} | ||
else { | ||
result.push({ | ||
rules, | ||
enable, | ||
pos: lineStarts[line], | ||
@@ -252,15 +233,41 @@ // no need to switch back if there is no next line | ||
}); | ||
break; | ||
} | ||
break; | ||
case '-next-line': { | ||
const lineStarts = sourceFile.getLineStarts(); | ||
const line = ts.getLineAndCharacterOfPosition(sourceFile, comment.pos).line + 1; | ||
if (lineStarts.length === line) { | ||
// there is no next line, return an out-of-range switch that can be reported | ||
result.push({ | ||
rules, | ||
enable, | ||
pos: sourceFile.end + 1, | ||
end: undefined, | ||
location: { pos: comment.pos, end: comment.end }, | ||
}); | ||
} | ||
else { | ||
result.push({ | ||
rules, | ||
enable, | ||
pos: lineStarts[line], | ||
// no need to switch back if there is no next line | ||
end: lineStarts.length === line + 1 ? undefined : lineStarts[line + 1], | ||
location: { pos: comment.pos, end: comment.end }, | ||
}); | ||
} | ||
break; | ||
} | ||
default: | ||
result.push({ rules, enable, pos: comment.pos, end: undefined, location: { pos: comment.pos, end: comment.end } }); | ||
} | ||
default: | ||
result.push({ rules, enable, pos: comment.pos, end: undefined, location: { pos: comment.pos, end: comment.end } }); | ||
} | ||
return result; | ||
} | ||
return result; | ||
} | ||
}; | ||
DefaultLineSwitchParser = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], DefaultLineSwitchParser); | ||
}; | ||
DefaultLineSwitchParser = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], DefaultLineSwitchParser); | ||
return DefaultLineSwitchParser; | ||
})(); | ||
exports.DefaultLineSwitchParser = DefaultLineSwitchParser; | ||
@@ -267,0 +274,0 @@ function parseRules(raw, offset) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ConsoleMessageHandler = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -7,21 +8,24 @@ const ymir_1 = require("@fimbul/ymir"); | ||
const utils_1 = require("../../utils"); | ||
let ConsoleMessageHandler = class ConsoleMessageHandler { | ||
constructor() { | ||
this.warned = []; | ||
} | ||
log(message) { | ||
console.log(message); | ||
} | ||
warn(message) { | ||
if (utils_1.addUnique(this.warned, message)) | ||
console.warn(message); | ||
} | ||
error(e) { | ||
console.error(e instanceof ymir_1.ConfigurationError ? e.message : e); | ||
} | ||
}; | ||
ConsoleMessageHandler = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], ConsoleMessageHandler); | ||
let ConsoleMessageHandler = /** @class */ (() => { | ||
let ConsoleMessageHandler = class ConsoleMessageHandler { | ||
constructor() { | ||
this.warned = []; | ||
} | ||
log(message) { | ||
console.log(message); | ||
} | ||
warn(message) { | ||
if (utils_1.addUnique(this.warned, message)) | ||
console.warn(message); | ||
} | ||
error(e) { | ||
console.error(e instanceof ymir_1.ConfigurationError ? e.message : e); | ||
} | ||
}; | ||
ConsoleMessageHandler = tslib_1.__decorate([ | ||
inversify_1.injectable() | ||
], ConsoleMessageHandler); | ||
return ConsoleMessageHandler; | ||
})(); | ||
exports.ConsoleMessageHandler = ConsoleMessageHandler; | ||
//# sourceMappingURL=message-handler.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NodeResolver = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -8,32 +9,35 @@ const inversify_1 = require("inversify"); | ||
const resolve = require("resolve"); | ||
let NodeResolver = class NodeResolver { | ||
constructor(fs, directories) { | ||
this.fs = fs; | ||
this.directories = directories; | ||
this.defaultExtensions = Object.keys(require.extensions).filter((ext) => ext !== '.json' && ext !== '.node'); | ||
} | ||
getDefaultExtensions() { | ||
return this.defaultExtensions; | ||
} | ||
resolve(id, basedir = this.directories.getCurrentDirectory(), extensions = this.defaultExtensions, paths) { | ||
return resolve.sync(id, { | ||
basedir, | ||
extensions, | ||
paths, | ||
isFile: (file) => this.fs.isFile(file), | ||
readFileSync: (file) => this.fs.readFile(file), | ||
isDirectory: (file) => this.fs.isDirectory(file), | ||
}); | ||
} | ||
require(id, options) { | ||
if (options !== undefined && options.cache === false) | ||
delete require.cache[id]; | ||
return require(id); | ||
} | ||
}; | ||
NodeResolver = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [cached_file_system_1.CachedFileSystem, ymir_1.DirectoryService]) | ||
], NodeResolver); | ||
let NodeResolver = /** @class */ (() => { | ||
let NodeResolver = class NodeResolver { | ||
constructor(fs, directories) { | ||
this.fs = fs; | ||
this.directories = directories; | ||
this.defaultExtensions = Object.keys(require.extensions).filter((ext) => ext !== '.json' && ext !== '.node'); | ||
} | ||
getDefaultExtensions() { | ||
return this.defaultExtensions; | ||
} | ||
resolve(id, basedir = this.directories.getCurrentDirectory(), extensions = this.defaultExtensions, paths) { | ||
return resolve.sync(id, { | ||
basedir, | ||
extensions, | ||
paths, | ||
isFile: (file) => this.fs.isFile(file), | ||
readFileSync: (file) => this.fs.readFile(file), | ||
isDirectory: (file) => this.fs.isDirectory(file), | ||
}); | ||
} | ||
require(id, options) { | ||
if (options !== undefined && options.cache === false) | ||
delete require.cache[id]; | ||
return require(id); | ||
} | ||
}; | ||
NodeResolver = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [cached_file_system_1.CachedFileSystem, ymir_1.DirectoryService]) | ||
], NodeResolver); | ||
return NodeResolver; | ||
})(); | ||
exports.NodeResolver = NodeResolver; | ||
//# sourceMappingURL=resolver.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NodeRuleLoader = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -9,34 +10,37 @@ const ymir_1 = require("@fimbul/ymir"); | ||
const log = debug('wotan:ruleLoaderHost'); | ||
let NodeRuleLoader = class NodeRuleLoader { | ||
constructor(builtinResolver, resolver) { | ||
this.builtinResolver = builtinResolver; | ||
this.resolver = resolver; | ||
} | ||
loadCoreRule(name) { | ||
name = this.builtinResolver.resolveRule(name); | ||
try { | ||
name = this.resolver.resolve(name); | ||
let NodeRuleLoader = /** @class */ (() => { | ||
let NodeRuleLoader = class NodeRuleLoader { | ||
constructor(builtinResolver, resolver) { | ||
this.builtinResolver = builtinResolver; | ||
this.resolver = resolver; | ||
} | ||
catch { | ||
return; | ||
loadCoreRule(name) { | ||
name = this.builtinResolver.resolveRule(name); | ||
try { | ||
name = this.resolver.resolve(name); | ||
} | ||
catch { | ||
return; | ||
} | ||
log('Found %s', name); | ||
return this.resolver.require(name).Rule; | ||
} | ||
log('Found %s', name); | ||
return this.resolver.require(name).Rule; | ||
} | ||
loadCustomRule(name, directory) { | ||
try { | ||
name = this.resolver.resolve(path.join(directory, name), directory); | ||
loadCustomRule(name, directory) { | ||
try { | ||
name = this.resolver.resolve(path.join(directory, name), directory); | ||
} | ||
catch { | ||
return; | ||
} | ||
log('Found %s', name); | ||
return this.resolver.require(name).Rule; | ||
} | ||
catch { | ||
return; | ||
} | ||
log('Found %s', name); | ||
return this.resolver.require(name).Rule; | ||
} | ||
}; | ||
NodeRuleLoader = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.BuiltinResolver, ymir_1.Resolver]) | ||
], NodeRuleLoader); | ||
}; | ||
NodeRuleLoader = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.BuiltinResolver, ymir_1.Resolver]) | ||
], NodeRuleLoader); | ||
return NodeRuleLoader; | ||
})(); | ||
exports.NodeRuleLoader = NodeRuleLoader; | ||
//# sourceMappingURL=rule-loader-host.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.FormatterLoader = void 0; | ||
const tslib_1 = require("tslib"); | ||
const inversify_1 = require("inversify"); | ||
const ymir_1 = require("@fimbul/ymir"); | ||
let FormatterLoader = class FormatterLoader { | ||
constructor(host, directories) { | ||
this.host = host; | ||
this.directories = directories; | ||
} | ||
loadFormatter(name) { | ||
let formatter; | ||
if (/^[a-zA-Z-]+$/.test(name)) | ||
formatter = this.host.loadCoreFormatter(name); | ||
if (formatter === undefined) | ||
formatter = this.host.loadCustomFormatter(name, this.directories.getCurrentDirectory()); | ||
if (formatter === undefined) | ||
throw new ymir_1.ConfigurationError(`Cannot find formatter '${name}' relative to '${this.directories.getCurrentDirectory()}'.`); | ||
return formatter; | ||
} | ||
}; | ||
FormatterLoader = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.FormatterLoaderHost, ymir_1.DirectoryService]) | ||
], FormatterLoader); | ||
let FormatterLoader = /** @class */ (() => { | ||
let FormatterLoader = class FormatterLoader { | ||
constructor(host, directories) { | ||
this.host = host; | ||
this.directories = directories; | ||
} | ||
loadFormatter(name) { | ||
let formatter; | ||
if (/^[a-zA-Z-]+$/.test(name)) | ||
formatter = this.host.loadCoreFormatter(name); | ||
if (formatter === undefined) | ||
formatter = this.host.loadCustomFormatter(name, this.directories.getCurrentDirectory()); | ||
if (formatter === undefined) | ||
throw new ymir_1.ConfigurationError(`Cannot find formatter '${name}' relative to '${this.directories.getCurrentDirectory()}'.`); | ||
return formatter; | ||
} | ||
}; | ||
FormatterLoader = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.FormatterLoaderHost, ymir_1.DirectoryService]) | ||
], FormatterLoader); | ||
return FormatterLoader; | ||
})(); | ||
exports.FormatterLoader = FormatterLoader; | ||
//# sourceMappingURL=formatter-loader.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ProcessorLoader = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -7,21 +8,24 @@ const ymir_1 = require("@fimbul/ymir"); | ||
const utils_1 = require("../utils"); | ||
let ProcessorLoader = class ProcessorLoader { | ||
constructor(resolver, cache) { | ||
this.resolver = resolver; | ||
this.cache = cache.create(); | ||
} | ||
loadProcessor(path) { | ||
return utils_1.resolveCachedResult(this.cache, path, (p) => { | ||
const result = this.resolver.require(p).Processor; | ||
if (result === undefined) | ||
throw new ymir_1.ConfigurationError(`'${p}' has no export named 'Processor'.`); | ||
return result; | ||
}); | ||
} | ||
}; | ||
ProcessorLoader = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.Resolver, ymir_1.CacheFactory]) | ||
], ProcessorLoader); | ||
let ProcessorLoader = /** @class */ (() => { | ||
let ProcessorLoader = class ProcessorLoader { | ||
constructor(resolver, cache) { | ||
this.resolver = resolver; | ||
this.cache = cache.create(); | ||
} | ||
loadProcessor(path) { | ||
return utils_1.resolveCachedResult(this.cache, path, (p) => { | ||
const result = this.resolver.require(p).Processor; | ||
if (result === undefined) | ||
throw new ymir_1.ConfigurationError(`'${p}' has no export named 'Processor'.`); | ||
return result; | ||
}); | ||
} | ||
}; | ||
ProcessorLoader = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.Resolver, ymir_1.CacheFactory]) | ||
], ProcessorLoader); | ||
return ProcessorLoader; | ||
})(); | ||
exports.ProcessorLoader = ProcessorLoader; | ||
//# sourceMappingURL=processor-loader.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.RuleLoader = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -10,50 +11,53 @@ const inversify_1 = require("inversify"); | ||
const log = debug('wotan:ruleLoader'); | ||
let RuleLoader = class RuleLoader { | ||
constructor(host, logger, cache) { | ||
this.host = host; | ||
this.logger = logger; | ||
this.cache = cache.create(); | ||
} | ||
loadRule(name, directories) { | ||
if (directories === undefined) { | ||
const ctor = utils_1.resolveCachedResult(this.cache, name, this.loadCoreRule); | ||
if (ctor === undefined) | ||
this.logger.warn(`Could not find core rule '${name}'.`); | ||
return ctor; | ||
let RuleLoader = /** @class */ (() => { | ||
let RuleLoader = class RuleLoader { | ||
constructor(host, logger, cache) { | ||
this.host = host; | ||
this.logger = logger; | ||
this.cache = cache.create(); | ||
} | ||
for (const dir of directories) { | ||
const ctor = utils_1.resolveCachedResult(this.cache, `${dir}💩${name}`, this.loadCustomRule); | ||
if (ctor !== undefined) | ||
loadRule(name, directories) { | ||
if (directories === undefined) { | ||
const ctor = utils_1.resolveCachedResult(this.cache, name, this.loadCoreRule); | ||
if (ctor === undefined) | ||
this.logger.warn(`Could not find core rule '${name}'.`); | ||
return ctor; | ||
} | ||
for (const dir of directories) { | ||
const ctor = utils_1.resolveCachedResult(this.cache, `${dir}💩${name}`, this.loadCustomRule); | ||
if (ctor !== undefined) | ||
return ctor; | ||
} | ||
this.logger.warn(`Could not find rule '${name}' in ${directories}.`); | ||
return; | ||
} | ||
this.logger.warn(`Could not find rule '${name}' in ${directories}.`); | ||
return; | ||
} | ||
loadCoreRule(name) { | ||
log('Loading core rule %s', name); | ||
return this.host.loadCoreRule(name); | ||
} | ||
loadCustomRule(cacheKey) { | ||
const [directory, name] = cacheKey.split('💩'); | ||
log('Looking for %s in directory %s', name, directory); | ||
return this.host.loadCustomRule(name, directory); | ||
} | ||
}; | ||
tslib_1.__decorate([ | ||
bind_decorator_1.default, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", void 0) | ||
], RuleLoader.prototype, "loadCoreRule", null); | ||
tslib_1.__decorate([ | ||
bind_decorator_1.default, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", void 0) | ||
], RuleLoader.prototype, "loadCustomRule", null); | ||
RuleLoader = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.RuleLoaderHost, ymir_1.MessageHandler, ymir_1.CacheFactory]) | ||
], RuleLoader); | ||
loadCoreRule(name) { | ||
log('Loading core rule %s', name); | ||
return this.host.loadCoreRule(name); | ||
} | ||
loadCustomRule(cacheKey) { | ||
const [directory, name] = cacheKey.split('💩'); | ||
log('Looking for %s in directory %s', name, directory); | ||
return this.host.loadCustomRule(name, directory); | ||
} | ||
}; | ||
tslib_1.__decorate([ | ||
bind_decorator_1.default, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", void 0) | ||
], RuleLoader.prototype, "loadCoreRule", null); | ||
tslib_1.__decorate([ | ||
bind_decorator_1.default, | ||
tslib_1.__metadata("design:type", Function), | ||
tslib_1.__metadata("design:paramtypes", [String]), | ||
tslib_1.__metadata("design:returntype", void 0) | ||
], RuleLoader.prototype, "loadCustomRule", null); | ||
RuleLoader = tslib_1.__decorate([ | ||
inversify_1.injectable(), | ||
tslib_1.__metadata("design:paramtypes", [ymir_1.RuleLoaderHost, ymir_1.MessageHandler, ymir_1.CacheFactory]) | ||
], RuleLoader); | ||
return RuleLoader; | ||
})(); | ||
exports.RuleLoader = RuleLoader; | ||
//# sourceMappingURL=rule-loader.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getOutputFileNamesOfProjectReference = exports.iterateProjectReferences = exports.invertChangeRange = exports.hasParseErrors = exports.addUnique = exports.flatMap = exports.mapDefined = exports.hasSupportedExtension = exports.calculateChangeRange = exports.assertNever = exports.format = exports.unixifyPath = exports.resolveCachedResult = exports.arrayify = exports.OFFSET_TO_NODE_MODULES = void 0; | ||
const json5 = require("json5"); | ||
@@ -4,0 +5,0 @@ const yaml = require("js-yaml"); |
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
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
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
346603
3.79%4286
2.95%