You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
5.2 KiB
203 lines
5.2 KiB
"use strict"; |
|
|
|
Object.defineProperty(exports, "__esModule", { |
|
value: true |
|
}); |
|
exports.default = void 0; |
|
|
|
var _process = _interopRequireDefault(require("process")); |
|
|
|
var _path = require("path"); |
|
|
|
var _fsExtra = require("fs-extra"); |
|
|
|
var _loaderUtils = require("loader-utils"); |
|
|
|
var _ESLintError = _interopRequireDefault(require("./ESLintError")); |
|
|
|
var _createEngine = _interopRequireDefault(require("./createEngine")); |
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } |
|
|
|
class Linter { |
|
constructor(loaderContext, options) { |
|
this.loaderContext = loaderContext; |
|
this.options = options; |
|
this.resourcePath = this.parseResourcePath(); |
|
const { |
|
CLIEngine, |
|
engine |
|
} = (0, _createEngine.default)(options); |
|
this.CLIEngine = CLIEngine; |
|
this.engine = engine; |
|
} |
|
|
|
parseResourcePath() { |
|
const cwd = _process.default.cwd(); |
|
|
|
let { |
|
resourcePath |
|
} = this.loaderContext; // remove cwd from resource path in case webpack has been started from project |
|
// root, to allow having relative paths in .eslintignore |
|
// istanbul ignore next |
|
|
|
if (resourcePath.indexOf(cwd) === 0) { |
|
resourcePath = resourcePath.substr(cwd.length + (cwd === '/' ? 0 : 1)); |
|
} |
|
|
|
return resourcePath; |
|
} |
|
|
|
lint(content) { |
|
try { |
|
return this.engine.executeOnText(content, this.resourcePath, true); |
|
} catch (_) { |
|
this.getEmitter(false)(_); |
|
return { |
|
src: content |
|
}; |
|
} |
|
} |
|
|
|
printOutput(data) { |
|
const { |
|
options |
|
} = this; // skip ignored file warning |
|
|
|
if (this.constructor.skipIgnoredFileWarning(data)) { |
|
return; |
|
} // quiet filter done now |
|
// eslint allow rules to be specified in the input between comments |
|
// so we can found warnings defined in the input itself |
|
|
|
|
|
const res = this.filter(data); // if enabled, use eslint auto-fixing where possible |
|
|
|
if (options.fix) { |
|
this.autoFix(res); |
|
} // skip if no errors or warnings |
|
|
|
|
|
if (res.errorCount < 1 && res.warningCount < 1) { |
|
return; |
|
} |
|
|
|
const results = this.parseResults(res); // Do not analyze if there are no results or eslint config |
|
|
|
if (!results) { |
|
return; |
|
} |
|
|
|
const messages = options.formatter(results); |
|
this.reportOutput(results, messages); |
|
this.failOnErrorOrWarning(res, messages); |
|
const emitter = this.getEmitter(res); |
|
emitter(new _ESLintError.default(messages)); |
|
} |
|
|
|
static skipIgnoredFileWarning(res) { |
|
return res.warningCount === 1 && res.results[0].messages[0] && res.results[0].messages[0].message && res.results[0].messages[0].message.indexOf('ignore') > 1; |
|
} |
|
|
|
filter(data) { |
|
const res = data; // quiet filter done now |
|
// eslint allow rules to be specified in the input between comments |
|
// so we can found warnings defined in the input itself |
|
|
|
if (this.options.quiet && res.warningCount) { |
|
res.warningCount = 0; |
|
res.results[0].warningCount = 0; |
|
res.results[0].messages = res.results[0].messages.filter(message => message.severity !== 1); |
|
} |
|
|
|
return res; |
|
} |
|
|
|
autoFix(res) { |
|
if (res.results[0].output !== res.src || res.results[0].fixableErrorCount > 0 || res.results[0].fixableWarningCount > 0) { |
|
this.CLIEngine.outputFixes(res); |
|
} |
|
} |
|
|
|
parseResults({ |
|
results |
|
}) { |
|
// add filename for each results so formatter can have relevant filename |
|
if (results) { |
|
results.forEach(r => { |
|
// eslint-disable-next-line no-param-reassign |
|
r.filePath = this.loaderContext.resourcePath; |
|
}); |
|
} |
|
|
|
return results; |
|
} |
|
|
|
reportOutput(results, messages) { |
|
const { |
|
outputReport |
|
} = this.options; |
|
|
|
if (!outputReport || !outputReport.filePath) { |
|
return; |
|
} |
|
|
|
let content = messages; // if a different formatter is passed in as an option use that |
|
|
|
if (outputReport.formatter) { |
|
content = outputReport.formatter(results); |
|
} |
|
|
|
let filePath = (0, _loaderUtils.interpolateName)(this.loaderContext, outputReport.filePath, { |
|
content |
|
}); |
|
|
|
if (!(0, _path.isAbsolute)(filePath)) { |
|
filePath = (0, _path.join)( // eslint-disable-next-line no-underscore-dangle |
|
this.loaderContext._compiler.options.output.path, filePath); |
|
} |
|
|
|
(0, _fsExtra.ensureFileSync)(filePath); |
|
(0, _fsExtra.writeFileSync)(filePath, content); |
|
} |
|
|
|
failOnErrorOrWarning({ |
|
errorCount, |
|
warningCount |
|
}, messages) { |
|
const { |
|
failOnError, |
|
failOnWarning |
|
} = this.options; |
|
|
|
if (failOnError && errorCount) { |
|
throw new _ESLintError.default(`Module failed because of a eslint error.\n${messages}`); |
|
} |
|
|
|
if (failOnWarning && warningCount) { |
|
throw new _ESLintError.default(`Module failed because of a eslint warning.\n${messages}`); |
|
} |
|
} |
|
|
|
getEmitter({ |
|
errorCount |
|
}) { |
|
const { |
|
options, |
|
loaderContext |
|
} = this; // default behavior: emit error only if we have errors |
|
|
|
let emitter = errorCount ? loaderContext.emitError : loaderContext.emitWarning; // force emitError or emitWarning if user want this |
|
|
|
if (options.emitError) { |
|
emitter = loaderContext.emitError; |
|
} else if (options.emitWarning) { |
|
emitter = loaderContext.emitWarning; |
|
} |
|
|
|
return emitter; |
|
} |
|
|
|
} |
|
|
|
exports.default = Linter; |