eslint-plugin-html
Version:
A ESLint plugin to lint and fix inline scripts contained in HTML files.
163 lines (138 loc) • 4.31 kB
JavaScript
const getSettings = require("./settings").getSettings
const getFileMode = require("./getFileMode")
const extract = require("./extract")
const { remapMessages } = require("./remapMessages")
const { verifyWithSharedScopes } = require("./verifyWithSharedScopes")
const PREPARE_RULE_NAME = "__eslint-plugin-html-prepare"
module.exports = { createVerifyPatch }
function createVerifyPatch(verify) {
return function (textOrSourceCode, config, filenameOrOptions, saveState) {
const callOriginalVerify = () =>
verify.call(this, textOrSourceCode, config, filenameOrOptions, saveState)
if (typeof config.extractConfig === "function") {
return callOriginalVerify()
}
const pluginSettings = getSettings(config.settings || {})
const mode = getFileMode(pluginSettings, filenameOrOptions)
if (!mode || typeof textOrSourceCode !== "string") {
return callOriginalVerify()
}
let messages
;[messages, config] = verifyExternalHtmlPlugin(config, callOriginalVerify)
if (config.parser && config.parser.id === "@html-eslint/parser") {
messages.push(...callOriginalVerify())
const rules = {}
for (const name in config.rules) {
if (!name.startsWith("@html-eslint/")) {
rules[name] = config.rules[name]
}
}
config = editConfig(config, {
parser: null,
rules,
})
}
const extractResult = extract(
textOrSourceCode,
mode === "xml",
pluginSettings
)
if (pluginSettings.reportBadIndent) {
messages.push(
...extractResult.badIndentationLines.map((line) => ({
message: "Bad line indentation.",
line,
column: 1,
ruleId: "(html plugin)",
severity: pluginSettings.reportBadIndent,
}))
)
}
// Save code parts parsed source code so we don't have to parse it twice
const sourceCodes = new WeakMap()
const verifyCodePart = (codePart, { prepare, ignoreRules } = {}) => {
this.defineRule(PREPARE_RULE_NAME, (context) => {
sourceCodes.set(codePart, context.getSourceCode())
return {
Program(program) {
if (prepare) {
prepare(context, program)
}
},
}
})
const localMessages = verify.call(
this,
sourceCodes.get(codePart) || String(codePart),
editConfig(config, {
rules: Object.assign(
{ [PREPARE_RULE_NAME]: "error" },
!ignoreRules && config.rules
),
}),
ignoreRules && typeof filenameOrOptions === "object"
? Object.assign({}, filenameOrOptions, {
reportUnusedDisableDirectives: false,
})
: filenameOrOptions,
saveState
)
messages.push(
...remapMessages(localMessages, extractResult.hasBOM, codePart)
)
}
const parserOptions = config.parserOptions || {}
if (parserOptions.sourceType === "module") {
for (const codePart of extractResult.code) {
verifyCodePart(codePart)
}
} else {
verifyWithSharedScopes(extractResult.code, verifyCodePart, parserOptions)
}
messages.sort((ma, mb) => ma.line - mb.line || ma.column - mb.column)
return messages
}
}
function editConfig(config, { parser = config.parser, rules = config.rules }) {
return {
...config,
parser,
rules,
}
}
const externalHtmlPluginPrefixes = [
"@html-eslint/",
"@angular-eslint/template-",
]
function getParserId(config) {
if (!config.parser) {
return
}
if (typeof config.parser === "string") {
// old versions of ESLint (ex: 4.7)
return config.parser
}
return config.parser.id
}
function verifyExternalHtmlPlugin(config, callOriginalVerify) {
const parserId = getParserId(config)
const externalHtmlPluginPrefix =
parserId &&
externalHtmlPluginPrefixes.find((prefix) => parserId.startsWith(prefix))
if (!externalHtmlPluginPrefix) {
return [[], config]
}
const rules = {}
for (const name in config.rules) {
if (!name.startsWith(externalHtmlPluginPrefix)) {
rules[name] = config.rules[name]
}
}
return [
callOriginalVerify(),
editConfig(config, {
parser: null,
rules,
}),
]
}