Socket
Socket
Sign inDemoInstall

eslint-plugin-html

Package Overview
Dependencies
Maintainers
1
Versions
58
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-html - npm Package Compare versions

Comparing version 4.0.5 to 5.0.0-alpha.0

5

CHANGELOG.md

@@ -0,1 +1,6 @@

UNRELEASED v5
* **Breaking: drop support of Node.js 4**
* Introduce the `html/fake-file-extension` setting to support the latest version of `eslint-plugin-prettier`
* More detailed error reporting
2018-06-20 v4.0.5

@@ -2,0 +7,0 @@ * Fix typo regression from v4.0.4

8

package.json
{
"name": "eslint-plugin-html",
"version": "4.0.5",
"version": "5.0.0-alpha.0",
"description": "A ESLint plugin to lint and fix inline scripts contained in HTML files.",

@@ -23,6 +23,6 @@ "license": "ISC",

"devDependencies": {
"eslint": "^4.7.0",
"eslint": "^5.0.0",
"eslint-config-benoitz-prettier": "^1.0.1",
"jest": "^21.1.0",
"prettier": "^1.7.4"
"jest": "^23.1.0",
"prettier": "^1.13.5"
},

@@ -29,0 +29,0 @@ "scripts": {

@@ -198,2 +198,22 @@ eslint-plugin-html

### `html/fake-file-extension`
Some other `ESLint` plugin are basing their behavior on the file extension. For example,
`eslint-plugin-prettier` will format the script according to the file type determined by the file
extension. By default, the file name won't be changed, so if the file extension is `.html`,
`Prettier` will fail to handle the script because it will try to parse it as HTML, even if it will
receive only the JavaScript parts. This setting will override the file extension reported to other
plugins.
```javascript
{
"plugins": [ "html" ],
"settings": {
"html/fake-file-extension": true, // will replace the file extension with ".js"
"html/fake-file-extension": ".jsx", // will replace the file extension with ".jsx"
}
}
```
Troubleshooting

@@ -227,1 +247,6 @@ ---------------

`eslint-plugin-html` from your dependencies if you still have this.
### Compatibility with `eslint-plugin-prettier`
Use the setting [`html/fake-file-extension`](#htmlfake-file-extension).

@@ -7,4 +7,4 @@ "use strict"

function execute(file, baseConfig) {
if (!baseConfig) baseConfig = {}
function execute(file, options) {
if (!options) options = {}

@@ -14,3 +14,3 @@ const cli = new CLIEngine({

baseConfig: {
settings: baseConfig.settings,
settings: options.settings,
rules: Object.assign(

@@ -20,16 +20,19 @@ {

},
baseConfig.rules
options.rules
),
globals: baseConfig.globals,
env: baseConfig.env,
parserOptions: baseConfig.parserOptions,
globals: options.globals,
env: options.env,
parserOptions: options.parserOptions,
},
ignore: false,
useEslintrc: false,
fix: baseConfig.fix,
fix: options.fix,
})
cli.addPlugin("html", plugin)
if (options.mockPlugin) {
cli.addPlugin("mock", options.mockPlugin)
}
const results = cli.executeOnFiles([path.join(__dirname, "fixtures", file)])
.results[0]
return baseConfig.fix ? results : results && results.messages
return options.fix ? results : results && results.messages
}

@@ -331,2 +334,66 @@

describe("html/fake-file-extension setting", () => {
const filenames = []
function config(settingValue) {
return {
rules: {
"mock/foo": "error",
},
settings: {
"html/fake-file-extension": settingValue,
},
mockPlugin: {
rules: {
foo: {
create(context) {
filenames.push(context.getFilename())
return {}
},
},
},
},
}
}
beforeEach(() => {
filenames.length = 0
})
it("fakes the file extension", () => {
execute("simple.html", config(true))
const filePath = path.resolve(__dirname, "fixtures/simple.js")
expect(filenames).toEqual([
filePath,
filePath,
filePath,
filePath,
filePath,
])
})
it("doesn't fake the file extension", () => {
execute("simple.html", config(false))
const filePath = path.resolve(__dirname, "fixtures/simple.html")
expect(filenames).toEqual([
filePath,
filePath,
filePath,
filePath,
filePath,
])
})
it("set a custom file extension", () => {
execute("simple.html", config(".jsx"))
const filePath = path.resolve(__dirname, "fixtures/simple.jsx")
expect(filenames).toEqual([
filePath,
filePath,
filePath,
filePath,
filePath,
])
})
})
describe("xml support", () => {

@@ -333,0 +400,0 @@ it("consider .html files as HTML", () => {

@@ -223,2 +223,3 @@ "use strict"

badIndentationLines,
hasBOM: code.startsWith("\uFEFF"),
}

@@ -225,0 +226,0 @@ }

@@ -9,5 +9,3 @@ "use strict"

const BOM = "\uFEFF"
const GET_SCOPE_RULE_NAME = "__eslint-plugin-html-get-scope"
const DECLARE_VARIABLES_RULE_NAME = "__eslint-plugin-html-declare-variables"
const PREPARE_RULE_NAME = "__eslint-plugin-html-prepare"
const LINTER_ISPATCHED_PROPERTY_NAME =

@@ -79,3 +77,4 @@ "__eslint-plugin-html-verify-function-is-patched"

const pluginVersion = require("../package.json").version
const parentPaths = module =>
module ? [module.filename].concat(parentPaths(module.parent)) : []

@@ -90,9 +89,10 @@ throw new Error(

* ESLint path: ${eslintPath}
* Plugin version: ${pluginVersion}
* Plugin path: ${__dirname}
* Plugin version: ${require("../package.json").version}
* Plugin inclusion paths: ${parentPaths(module).join(", ")}
* NodeJS version: ${process.version}
* Content of your lock file (package-lock.json or yarn.lock)
* CLI arguments: ${JSON.stringify(process.argv)}
* Content of your lock file (package-lock.json or yarn.lock) or the output of \`npm list\`
* How did you run ESLint (via the command line? an editor plugin?)
* The following stack trace:
${new Error().stack}
${new Error().stack.slice(10)}

@@ -105,2 +105,36 @@

function getMode(pluginSettings, filenameOrOptions) {
const filename =
typeof filenameOrOptions === "object"
? filenameOrOptions.filename
: filenameOrOptions
const extension = path.extname(filename || "")
if (pluginSettings.htmlExtensions.indexOf(extension) >= 0) {
return "html"
}
if (pluginSettings.xmlExtensions.indexOf(extension) >= 0) {
return "xml"
}
}
function replaceExtension(fileName, extension) {
const index = fileName.lastIndexOf(".")
const fileNameWithoutExtension =
index < 0 ? fileName : fileName.slice(0, index)
return fileNameWithoutExtension + extension
}
function fakeFileExtension(filenameOrOptions, extension) {
if (!extension) return filenameOrOptions
if (typeof filenameOrOptions === "object") {
return Object.assign({}, filenameOrOptions, {
filename: replaceExtension(filenameOrOptions.filename, extension),
})
}
return replaceExtension(filenameOrOptions, extension)
}
function patch(Linter) {

@@ -120,70 +154,77 @@ const verify = Linter.prototype.verify

) {
const localVerify = code =>
verify.call(this, code, config, filenameOrOptions, saveState)
const pluginSettings = getSettings(config.settings || {})
const mode = getMode(pluginSettings, filenameOrOptions)
let messages
const filename =
typeof filenameOrOptions === "object"
? filenameOrOptions.filename
: filenameOrOptions
const extension = path.extname(filename || "")
if (!mode || typeof textOrSourceCode !== "string") {
return verify.call(
this,
textOrSourceCode,
config,
filenameOrOptions,
saveState
)
}
const extractResult = extract(
textOrSourceCode,
pluginSettings.indent,
mode === "xml",
pluginSettings.isJavaScriptMIMEType
)
const pluginSettings = getSettings(config.settings || {})
const isHTML = pluginSettings.htmlExtensions.indexOf(extension) >= 0
const isXML =
!isHTML && pluginSettings.xmlExtensions.indexOf(extension) >= 0
const messages = []
if (typeof textOrSourceCode === "string" && (isHTML || isXML)) {
messages = []
if (pluginSettings.reportBadIndent) {
messages.push(
...extractResult.badIndentationLines.map(line => ({
message: "Bad line indentation.",
line,
column: 1,
ruleId: "(html plugin)",
severity: pluginSettings.reportBadIndent,
}))
)
}
const pushMessages = (localMessages, code) => {
messages.push.apply(
messages,
remapMessages(localMessages, textOrSourceCode.startsWith(BOM), code)
)
}
// 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.rules.define(PREPARE_RULE_NAME, context => {
sourceCodes.set(codePart, context.getSourceCode())
return {
Program() {
if (prepare) {
prepare(context)
}
},
}
})
const currentInfos = extract(
textOrSourceCode,
pluginSettings.indent,
isXML,
pluginSettings.isJavaScriptMIMEType
const localMessages = verify.call(
this,
sourceCodes.get(codePart) || String(codePart),
Object.assign({}, config, {
rules: Object.assign(
{ [PREPARE_RULE_NAME]: "error" },
!ignoreRules && config.rules
),
}),
fakeFileExtension(filenameOrOptions, pluginSettings.fakeFileExtension),
saveState
)
if (pluginSettings.reportBadIndent) {
currentInfos.badIndentationLines.forEach(line => {
messages.push({
message: "Bad line indentation.",
line,
column: 1,
ruleId: "(html plugin)",
severity: pluginSettings.reportBadIndent,
})
})
}
messages.push(
...remapMessages(localMessages, extractResult.hasBOM, codePart)
)
}
if (
config.parserOptions &&
config.parserOptions.sourceType === "module"
) {
for (const code of currentInfos.code) {
pushMessages(localVerify(String(code)), code)
}
} else {
verifyWithSharedScopes.call(
this,
localVerify,
config,
currentInfos,
pushMessages
)
if (config.parserOptions && config.parserOptions.sourceType === "module") {
for (const codePart of extractResult.code) {
verifyCodePart(codePart)
}
messages.sort((ma, mb) => {
return ma.line - mb.line || ma.column - mb.column
})
} else {
messages = localVerify(textOrSourceCode)
verifyWithSharedScopes(extractResult.code, verifyCodePart)
}
messages.sort((ma, mb) => ma.line - mb.line || ma.column - mb.column)
return messages

@@ -193,72 +234,49 @@ }

function verifyWithSharedScopes(
localVerify,
config,
currentInfos,
pushMessages
) {
function verifyWithSharedScopes(codeParts, verifyCodePart) {
// First pass: collect needed globals and declared globals for each script tags.
const firstPassValues = []
const originalRules = config.rules
config.rules = { [GET_SCOPE_RULE_NAME]: "error" }
for (const code of currentInfos.code) {
this.rules.define(GET_SCOPE_RULE_NAME, context => {
return {
Program() {
firstPassValues.push({
code,
sourceCode: context.getSourceCode(),
exportedGlobals: context
.getScope()
.through.map(node => node.identifier.name),
declaredGlobals: context
.getScope()
.variables.map(variable => variable.name),
})
},
}
for (const codePart of codeParts) {
verifyCodePart(codePart, {
prepare(context) {
firstPassValues.push({
codePart,
exportedGlobals: context
.getScope()
.through.map(node => node.identifier.name),
declaredGlobals: context
.getScope()
.variables.map(variable => variable.name),
})
},
ignoreRules: true,
})
pushMessages(localVerify(String(code)), code)
}
config.rules = Object.assign(
{ [DECLARE_VARIABLES_RULE_NAME]: "error" },
originalRules
)
// Second pass: declare variables for each script scope, then run eslint.
for (let i = 0; i < firstPassValues.length; i += 1) {
this.rules.define(DECLARE_VARIABLES_RULE_NAME, context => {
return {
Program() {
const exportedGlobals = splatSet(
firstPassValues
.slice(i + 1)
.map(nextValues => nextValues.exportedGlobals)
)
for (const name of exportedGlobals) context.markVariableAsUsed(name)
verifyCodePart(firstPassValues[i].codePart, {
prepare(context) {
const exportedGlobals = splatSet(
firstPassValues
.slice(i + 1)
.map(nextValues => nextValues.exportedGlobals)
)
for (const name of exportedGlobals) context.markVariableAsUsed(name)
const declaredGlobals = splatSet(
firstPassValues
.slice(0, i)
.map(previousValues => previousValues.declaredGlobals)
)
const scope = context.getScope()
scope.through = scope.through.filter(variable => {
return !declaredGlobals.has(variable.identifier.name)
})
},
}
const declaredGlobals = splatSet(
firstPassValues
.slice(0, i)
.map(previousValues => previousValues.declaredGlobals)
)
const scope = context.getScope()
scope.through = scope.through.filter(variable => {
return !declaredGlobals.has(variable.identifier.name)
})
},
})
const values = firstPassValues[i]
pushMessages(localVerify(values.sourceCode), values.code)
}
config.rules = originalRules
}
function remapMessages(messages, hasBOM, code) {
function remapMessages(messages, hasBOM, codePart) {
const newMessages = []

@@ -268,3 +286,3 @@ const bomOffset = hasBOM ? -1 : 0

for (const message of messages) {
const location = code.originalLocation({
const location = codePart.originalLocation({
line: message.line,

@@ -280,3 +298,3 @@ // eslint-plugin-eslint-comments is raising message with column=0 to bypass ESLint ignore

Object.assign(message, location)
message.source = code.getOriginalLine(location.line)
message.source = codePart.getOriginalLine(location.line)

@@ -286,6 +304,6 @@ // Map fix range

message.fix.range = [
code.originalIndex(message.fix.range[0]) + bomOffset,
codePart.originalIndex(message.fix.range[0]) + bomOffset,
// The range end is exclusive, meaning it should replace all characters with indexes from
// start to end - 1. We have to get the original index of the last targeted character.
code.originalIndex(message.fix.range[1] - 1) + 1 + bomOffset,
codePart.originalIndex(message.fix.range[1] - 1) + 1 + bomOffset,
]

@@ -296,3 +314,3 @@ }

if (message.endLine && message.endColumn) {
const endLocation = code.originalLocation({
const endLocation = codePart.originalLocation({
line: message.endLine,

@@ -299,0 +317,0 @@ column: message.endColumn,

@@ -53,2 +53,5 @@ "use strict"

let fakeFileExtension = getSetting(settings, "fake-file-extension")
if (fakeFileExtension === true) fakeFileExtension = ".js"
let reportBadIndent

@@ -108,2 +111,3 @@ switch (getSetting(settings, "report-bad-indent")) {

reportBadIndent,
fakeFileExtension,
isJavaScriptMIMEType,

@@ -110,0 +114,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc