eslint-plugin-svelte3
Advanced tools
Comparing version 2.7.2 to 2.7.3
@@ -0,1 +1,5 @@ | ||
# 2.7.3 | ||
- Fix mishandling of blocks whose last line consists of only the expected indentation | ||
# 2.7.2 | ||
@@ -2,0 +6,0 @@ |
265
index.js
'use strict'; | ||
const blocks = new Map(); | ||
const new_block = () => ({ transformed_code: '', line_offsets: null, translations: new Map() }); | ||
let custom_compiler, default_compiler, compiler_options, messages, ignore_warnings, ignore_styles, named_blocks, var_names; | ||
// get the total length, number of lines, and length of the last line of a string | ||
@@ -49,2 +45,5 @@ const get_offsets = str => { | ||
total_offsets.push(total_offsets[total_offsets.length - 1] + offsets[offsets.length - 1]); | ||
if (i >= str.length) { | ||
break; | ||
} | ||
} | ||
@@ -56,76 +55,16 @@ dedented += str[i]; | ||
// transform a linting message according to the module/instance script info we've gathered | ||
const transform_message = ({ transformed_code }, { unoffsets, dedent, offsets, range }, message) => { | ||
// strip out the start and end of the fix if they are not actually changes | ||
if (message.fix) { | ||
while (message.fix.range[0] < message.fix.range[1] && transformed_code[message.fix.range[0]] === message.fix.text[0]) { | ||
message.fix.range[0]++; | ||
message.fix.text = message.fix.text.slice(1); | ||
// get character offsets of each line in a string | ||
const get_line_offsets = str => { | ||
const offsets = [-1]; | ||
for (let i = 0; i < str.length; i++) { | ||
if (str[i] === '\n') { | ||
offsets.push(i); | ||
} | ||
while (message.fix.range[0] < message.fix.range[1] && transformed_code[message.fix.range[1] - 1] === message.fix.text[message.fix.text.length - 1]) { | ||
message.fix.range[1]--; | ||
message.fix.text = message.fix.text.slice(0, -1); | ||
} | ||
} | ||
// shift position reference backward according to unoffsets | ||
{ | ||
const { length, lines, last } = unoffsets; | ||
if (message.line === lines) { | ||
message.column -= last; | ||
} | ||
if (message.endColumn && message.endLine === lines) { | ||
message.endColumn -= last; | ||
} | ||
message.line -= lines - 1; | ||
if (message.endLine) { | ||
message.endLine -= lines - 1; | ||
} | ||
if (message.fix) { | ||
message.fix.range[0] -= length; | ||
message.fix.range[1] -= length; | ||
} | ||
} | ||
// adjust position reference according to the previous dedenting | ||
{ | ||
const { offsets, total_offsets } = dedent; | ||
message.column += offsets[message.line - 1]; | ||
if (message.endColumn) { | ||
message.endColumn += offsets[message.endLine - 1]; | ||
} | ||
if (message.fix) { | ||
message.fix.range[0] += total_offsets[message.line]; | ||
message.fix.range[1] += total_offsets[message.line]; | ||
} | ||
} | ||
// shift position reference forward according to offsets | ||
{ | ||
const { length, lines, last } = offsets; | ||
if (message.line === 1) { | ||
message.column += last; | ||
} | ||
if (message.endColumn && message.endLine === 1) { | ||
message.endColumn += last; | ||
} | ||
message.line += lines - 1; | ||
if (message.endLine) { | ||
message.endLine += lines - 1; | ||
} | ||
if (message.fix) { | ||
message.fix.range[0] += length; | ||
message.fix.range[1] += length; | ||
} | ||
} | ||
// make sure the fix doesn't include anything outside the range of the script | ||
if (message.fix) { | ||
if (message.fix.range[0] < range[0]) { | ||
message.fix.text = message.fix.text.slice(range[0] - message.fix.range[0]); | ||
message.fix.range[0] = range[0]; | ||
} | ||
if (message.fix.range[1] > range[1]) { | ||
message.fix.text = message.fix.text.slice(0, range[1] - message.fix.range[1]); | ||
message.fix.range[1] = range[1]; | ||
} | ||
} | ||
return offsets; | ||
}; | ||
// return a new block | ||
const new_block = () => ({ transformed_code: '', line_offsets: null, translations: new Map() }); | ||
// get translation info and include the processed scripts in this block's transformed_code | ||
@@ -147,2 +86,37 @@ const get_translation = (text, block, node, options = {}) => { | ||
const processor_options = {}; | ||
// find Linter instance | ||
const linter_path = Object.keys(require.cache).find(path => path.endsWith('/eslint/lib/linter/linter.js') || path.endsWith('\\eslint\\lib\\linter\\linter.js')); | ||
if (!linter_path) { | ||
throw new Error('Could not find ESLint Linter in require cache'); | ||
} | ||
const { Linter } = require(linter_path); | ||
// patch Linter#verify | ||
const { verify } = Linter.prototype; | ||
Linter.prototype.verify = function(code, config, options) { | ||
// fetch settings | ||
const settings = config && (typeof config.extractConfig === 'function' ? config.extractConfig(options.filename) : config).settings || {}; | ||
processor_options.custom_compiler = settings['svelte3/compiler']; | ||
processor_options.ignore_warnings = settings['svelte3/ignore-warnings']; | ||
processor_options.ignore_styles = settings['svelte3/ignore-styles']; | ||
processor_options.compiler_options = settings['svelte3/compiler-options']; | ||
processor_options.named_blocks = settings['svelte3/named-blocks']; | ||
// call original Linter#verify | ||
return verify.call(this, code, config, options); | ||
}; | ||
let state; | ||
const reset = () => { | ||
state = { | ||
messages: null, | ||
var_names: null, | ||
blocks: new Map(), | ||
}; | ||
}; | ||
reset(); | ||
let default_compiler; | ||
// find the contextual name or names described by a particular node in the AST | ||
@@ -168,4 +142,4 @@ const contextual_names = []; | ||
const preprocess = text => { | ||
const compiler = custom_compiler || default_compiler || (default_compiler = require('svelte/compiler')); | ||
if (ignore_styles) { | ||
const compiler = processor_options.custom_compiler || default_compiler || (default_compiler = require('svelte/compiler')); | ||
if (processor_options.ignore_styles) { | ||
// wipe the appropriate <style> tags in the file | ||
@@ -182,3 +156,3 @@ text = text.replace(/<style(\s[^]*?)?>[^]*?<\/style>/gi, (match, attributes = '') => { | ||
}); | ||
return ignore_styles(attrs) ? match.replace(/\S/g, ' ') : match; | ||
return processor_options.ignore_styles(attrs) ? match.replace(/\S/g, ' ') : match; | ||
}); | ||
@@ -189,6 +163,6 @@ } | ||
try { | ||
result = compiler.compile(text, { generate: false, ...compiler_options }); | ||
result = compiler.compile(text, { generate: false, ...processor_options.compiler_options }); | ||
} catch ({ name, message, start, end }) { | ||
// convert the error to a linting message, store it, and return | ||
messages = [ | ||
state.messages = [ | ||
{ | ||
@@ -208,6 +182,6 @@ ruleId: name, | ||
const references_and_reassignments = `{${vars.filter(v => v.referenced).map(v => v.name)};${vars.filter(v => v.reassigned || v.export_name).map(v => v.name + '=0')}}`; | ||
var_names = new Set(vars.map(v => v.name)); | ||
state.var_names = new Set(vars.map(v => v.name)); | ||
// convert warnings to linting messages | ||
messages = (ignore_warnings ? warnings.filter(warning => !ignore_warnings(warning)) : warnings).map(({ code, message, start, end }) => ({ | ||
state.messages = (processor_options.ignore_warnings ? warnings.filter(warning => !processor_options.ignore_warnings(warning)) : warnings).map(({ code, message, start, end }) => ({ | ||
ruleId: code, | ||
@@ -227,3 +201,3 @@ severity: 1, | ||
const block = new_block(); | ||
blocks.set('module.js', block); | ||
state.blocks.set('module.js', block); | ||
@@ -242,3 +216,3 @@ get_translation(text, block, ast.module.content); | ||
const block = new_block(); | ||
blocks.set('instance.js', block); | ||
state.blocks.set('instance.js', block); | ||
@@ -255,3 +229,3 @@ block.transformed_code = vars.filter(v => v.injected || v.module).map(v => `let ${v.name};`).join(''); | ||
const block = new_block(); | ||
blocks.set('template.js', block); | ||
state.blocks.set('template.js', block); | ||
@@ -304,5 +278,79 @@ block.transformed_code = vars.map(v => `let ${v.name};`).join(''); | ||
// return processed string | ||
return [...blocks].map(([filename, { transformed_code: text }]) => named_blocks ? { text, filename } : text); | ||
return [...state.blocks].map(([filename, { transformed_code: text }]) => processor_options.named_blocks ? { text, filename } : text); | ||
}; | ||
// transform a linting message according to the module/instance script info we've gathered | ||
const transform_message = ({ transformed_code }, { unoffsets, dedent, offsets, range }, message) => { | ||
// strip out the start and end of the fix if they are not actually changes | ||
if (message.fix) { | ||
while (message.fix.range[0] < message.fix.range[1] && transformed_code[message.fix.range[0]] === message.fix.text[0]) { | ||
message.fix.range[0]++; | ||
message.fix.text = message.fix.text.slice(1); | ||
} | ||
while (message.fix.range[0] < message.fix.range[1] && transformed_code[message.fix.range[1] - 1] === message.fix.text[message.fix.text.length - 1]) { | ||
message.fix.range[1]--; | ||
message.fix.text = message.fix.text.slice(0, -1); | ||
} | ||
} | ||
// shift position reference backward according to unoffsets | ||
{ | ||
const { length, lines, last } = unoffsets; | ||
if (message.line === lines) { | ||
message.column -= last; | ||
} | ||
if (message.endColumn && message.endLine === lines) { | ||
message.endColumn -= last; | ||
} | ||
message.line -= lines - 1; | ||
if (message.endLine) { | ||
message.endLine -= lines - 1; | ||
} | ||
if (message.fix) { | ||
message.fix.range[0] -= length; | ||
message.fix.range[1] -= length; | ||
} | ||
} | ||
// adjust position reference according to the previous dedenting | ||
{ | ||
const { offsets, total_offsets } = dedent; | ||
message.column += offsets[message.line - 1]; | ||
if (message.endColumn) { | ||
message.endColumn += offsets[message.endLine - 1]; | ||
} | ||
if (message.fix) { | ||
message.fix.range[0] += total_offsets[message.line]; | ||
message.fix.range[1] += total_offsets[message.line]; | ||
} | ||
} | ||
// shift position reference forward according to offsets | ||
{ | ||
const { length, lines, last } = offsets; | ||
if (message.line === 1) { | ||
message.column += last; | ||
} | ||
if (message.endColumn && message.endLine === 1) { | ||
message.endColumn += last; | ||
} | ||
message.line += lines - 1; | ||
if (message.endLine) { | ||
message.endLine += lines - 1; | ||
} | ||
if (message.fix) { | ||
message.fix.range[0] += length; | ||
message.fix.range[1] += length; | ||
} | ||
} | ||
// make sure the fix doesn't include anything outside the range of the script | ||
if (message.fix) { | ||
if (message.fix.range[0] < range[0]) { | ||
message.fix.text = message.fix.text.slice(range[0] - message.fix.range[0]); | ||
message.fix.range[0] = range[0]; | ||
} | ||
if (message.fix.range[1] > range[1]) { | ||
message.fix.text = message.fix.text.slice(0, range[1] - message.fix.range[1]); | ||
message.fix.range[1] = range[1]; | ||
} | ||
} | ||
}; | ||
// extract the string referenced by a message | ||
@@ -312,10 +360,5 @@ const get_referenced_string = (block, message) => { | ||
if (!block.line_offsets) { | ||
block.line_offsets = [-1, -1]; | ||
for (let i = 0; i < block.transformed_code.length; i++) { | ||
if (block.transformed_code[i] === '\n') { | ||
block.line_offsets.push(i); | ||
} | ||
} | ||
block.line_offsets = get_line_offsets(block.transformed_code); | ||
} | ||
return block.transformed_code.slice(block.line_offsets[message.line] + message.column, block.line_offsets[message.endLine] + message.endColumn); | ||
return block.transformed_code.slice(block.line_offsets[message.line - 1] + message.column, block.line_offsets[message.endLine - 1] + message.endColumn); | ||
} | ||
@@ -335,3 +378,3 @@ }; | ||
case 'no-restricted-syntax': return message.nodeType !== 'LabeledStatement' || get_identifier(get_referenced_string(block, message)) !== '$'; | ||
case 'no-self-assign': return !var_names.has(get_identifier(get_referenced_string(block, message))); | ||
case 'no-self-assign': return !state.var_names.has(get_identifier(get_referenced_string(block, message))); | ||
case 'no-unused-labels': return get_referenced_string(block, message) !== '$'; | ||
@@ -346,3 +389,3 @@ case 'quotes': return !translation.options.in_quoted_attribute; | ||
// filter messages and fix their offsets | ||
const blocks_array = [...blocks.values()]; | ||
const blocks_array = [...state.blocks.values()]; | ||
for (let i = 0; i < blocks_messages.length; i++) { | ||
@@ -355,3 +398,3 @@ const block = blocks_array[i]; | ||
transform_message(block, translation, message); | ||
messages.push(message); | ||
state.messages.push(message); | ||
} | ||
@@ -362,33 +405,9 @@ } | ||
// sort messages and return | ||
const sorted_messages = messages.sort((a, b) => a.line - b.line || a.column - b.column); | ||
blocks.clear(); | ||
custom_compiler = ignore_warnings = ignore_styles = compiler_options = messages = var_names = null; | ||
const sorted_messages = state.messages.sort((a, b) => a.line - b.line || a.column - b.column); | ||
reset(); | ||
return sorted_messages; | ||
}; | ||
/// PATCH THE LINTER - HACK TO GET ACCESS TO SETTINGS /// | ||
var index = { processors: { svelte3: { preprocess, postprocess, supportsAutofix: true } } }; | ||
// find Linter instance | ||
const linter_path = Object.keys(require.cache).find(path => path.endsWith('/eslint/lib/linter/linter.js') || path.endsWith('\\eslint\\lib\\linter\\linter.js')); | ||
if (!linter_path) { | ||
throw new Error('Could not find ESLint Linter in require cache'); | ||
} | ||
const { Linter } = require(linter_path); | ||
// patch Linter#verify | ||
const { verify } = Linter.prototype; | ||
Linter.prototype.verify = function(code, config, options) { | ||
// fetch settings | ||
const settings = config && (typeof config.extractConfig === 'function' ? config.extractConfig(options.filename) : config).settings || {}; | ||
custom_compiler = settings['svelte3/compiler']; | ||
ignore_warnings = settings['svelte3/ignore-warnings']; | ||
ignore_styles = settings['svelte3/ignore-styles']; | ||
compiler_options = settings['svelte3/compiler-options']; | ||
named_blocks = settings['svelte3/named-blocks']; | ||
// call original Linter#verify | ||
return verify.call(this, code, config, options); | ||
}; | ||
/// EXPORT THE PROCESSOR /// | ||
exports.processors = { svelte3: { preprocess, postprocess, supportsAutofix: true } }; | ||
module.exports = index; |
{ | ||
"name": "eslint-plugin-svelte3", | ||
"version": "2.7.2", | ||
"version": "2.7.3", | ||
"description": "An ESLint plugin for Svelte v3 components.", | ||
@@ -29,8 +29,11 @@ "keywords": [ | ||
"scripts": { | ||
"test": "node test" | ||
"build": "rollup -c", | ||
"dev": "rollup -cw", | ||
"test": "npm run build && node test" | ||
}, | ||
"devDependencies": { | ||
"eslint": ">=6.0.0", | ||
"rollup": "^1", | ||
"svelte": "^3.2.0" | ||
} | ||
} |
@@ -93,3 +93,3 @@ # eslint-plugin-svelte3 | ||
When an [ESLint processor](https://eslint.org/docs/user-guide/configuring#specifying-processor) processes a file, it is able to output named code blocks, which can each have their own linting configuration. When this setting is enabled, the code extracted from `<script context='module>` tag, the `<script>` tag, and the template are respectively given the block names `module.js`, `instance.js`, and `template.js`. | ||
When an [ESLint processor](https://eslint.org/docs/user-guide/configuring#specifying-processor) processes a file, it is able to output named code blocks, which can each have their own linting configuration. When this setting is enabled, the code extracted from `<script context='module'>` tag, the `<script>` tag, and the template are respectively given the block names `module.js`, `instance.js`, and `template.js`. | ||
@@ -96,0 +96,0 @@ This means that to override linting rules in Svelte components, you'd instead have to target `**/*.svelte/*.js`. But it also means that you can define an override targeting `**/*.svelte/*_template.js` for example, and that configuration will only apply to linting done on the templates in Svelte components. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25046
363
3