From a2f161170bb3157e83aa4a5d0a8872a577c73fa0 Mon Sep 17 00:00:00 2001 From: Nico Cevallos Date: Wed, 10 Jun 2020 18:17:40 -0300 Subject: [PATCH 1/5] add preprocess feature --- README.md | 7 ++ src/block.js | 9 ++ src/postprocess.js | 29 ++++++ src/preprocess.js | 196 +++++++++++++++++++++++++++++++++++++-- src/processor_options.js | 1 + src/state.js | 3 + 6 files changed, 237 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d609238..5294185 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,13 @@ This setting can be given the result of `require('.../path/to/svelte/compiler')` The default is `require('svelte/compiler')` from wherever the plugin is installed to. +### `svelte3/preprocess` + +You can use a preprocessor function to return custom AST info according to the original code. + +For now this only supports `module` and `instance` scripts. + + ## Using the CLI It's probably a good idea to make sure you can lint from the command line before proceeding with configuring your editor. diff --git a/src/block.js b/src/block.js index d6e75bc..eb08f63 100644 --- a/src/block.js +++ b/src/block.js @@ -1,5 +1,14 @@ import { get_offsets, dedent_code } from './utils.js'; +// find the index of the last element of an array matching a condition +export const find_last_index = (array, cond) => { + const idx = array.findIndex(item => !cond(item)); + return idx === -1 ? array.length - 1 : idx - 1; +}; + +// find the last element of an array matching a condition +export const find_last = (array, cond) => array[find_last_index(array, cond)]; + // return a new block export const new_block = () => ({ transformed_code: '', line_offsets: null, translations: new Map() }); diff --git a/src/postprocess.js b/src/postprocess.js index 875806f..e9f301c 100644 --- a/src/postprocess.js +++ b/src/postprocess.js @@ -1,5 +1,31 @@ import { state, reset } from './state.js'; import { get_line_offsets } from './utils.js'; +import { find_last_index, find_last } from './block.js' + +export const unmap = message => { + for (let j = 0; j < 2; j++) { + if (message[j ? 'endLine' : 'line']) { + const mapping = find_last(state.mappings[message[j ? 'endLine' : 'line'] - 1], ([column]) => column < message[j ? 'endColumn' : 'column']); + if (!mapping || mapping[1] !== 0) { + return false; + } + message[j ? 'endLine' : 'line'] = mapping[2] + 1; + message[j ? 'endColumn' : 'column'] += mapping[3] - mapping[0]; + } + } + if (message.fix) { + for (let j = 0; j < 2; j++) { + const line = find_last_index(state.post_line_offsets, offset => offset < message.fix.range[j]); + const line_offset = state.post_line_offsets[line]; + const mapping = find_last(state.mappings[line], ([column]) => column < message.fix.range[j] - line_offset); + if (!mapping || mapping[1] !== 0) { + return false; + } + message.fix.range[j] += mapping[3] - mapping[0] + state.pre_line_offsets[mapping[2]] - line_offset; + } + } + return true; +}; // transform a linting message according to the module/instance script info we've gathered const transform_message = ({ transformed_code }, { unoffsets, dedent, offsets, range }, message) => { @@ -118,6 +144,9 @@ export const postprocess = blocks_messages => { } } } + if (state.mappings) { + state.messages = state.messages.filter(unmap); + } // sort messages and return const sorted_messages = state.messages.sort((a, b) => a.line - b.line || a.column - b.column); diff --git a/src/preprocess.js b/src/preprocess.js index 77aa11c..d0f4ca3 100644 --- a/src/preprocess.js +++ b/src/preprocess.js @@ -1,7 +1,89 @@ import { new_block, get_translation } from './block.js'; import { processor_options } from './processor_options.js'; +import { get_line_offsets } from './utils.js'; import { state } from './state.js'; +var charToInteger = {}; +var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; +for (var i = 0; i < chars.length; i++) { + charToInteger[chars.charCodeAt(i)] = i; +} +function decode(mappings) { + var generatedCodeColumn = 0; // first field + var sourceFileIndex = 0; // second field + var sourceCodeLine = 0; // third field + var sourceCodeColumn = 0; // fourth field + var nameIndex = 0; // fifth field + var decoded = []; + var line = []; + var segment = []; + for (var i = 0, j = 0, shift = 0, value = 0, len = mappings.length; i < len; i++) { + var c = mappings.charCodeAt(i); + if (c === 44) { // "," + if (segment.length) + line.push(segment); + segment = []; + j = 0; + } + else if (c === 59) { // ";" + if (segment.length) + line.push(segment); + segment = []; + j = 0; + decoded.push(line); + line = []; + generatedCodeColumn = 0; + } + else { + var integer = charToInteger[c]; + if (integer === undefined) { + throw new Error('Invalid character (' + String.fromCharCode(c) + ')'); + } + var hasContinuationBit = integer & 32; + integer &= 31; + value += integer << shift; + if (hasContinuationBit) { + shift += 5; + } + else { + var shouldNegate = value & 1; + value >>>= 1; + if (shouldNegate) { + value = -value; + if (value === 0) + value = -0x80000000; + } + if (j == 0) { + generatedCodeColumn += value; + segment.push(generatedCodeColumn); + } + else if (j === 1) { + sourceFileIndex += value; + segment.push(sourceFileIndex); + } + else if (j === 2) { + sourceCodeLine += value; + segment.push(sourceCodeLine); + } + else if (j === 3) { + sourceCodeColumn += value; + segment.push(sourceCodeColumn); + } + else if (j === 4) { + nameIndex += value; + segment.push(nameIndex); + } + j++; + value = shift = 0; // reset + } + } + } + if (segment.length) + line.push(segment); + decoded.push(line); + return decoded; +} + let default_compiler; // find the contextual name or names described by a particular node in the AST @@ -23,7 +105,7 @@ const find_contextual_names = (compiler, node) => { }; // extract scripts to lint from component definition -export const preprocess = text => { +export const preprocess = (text, filename) => { const compiler = processor_options.custom_compiler || default_compiler || (default_compiler = require('svelte/compiler')); if (processor_options.ignore_styles) { // wipe the appropriate