From f001420dc7932390d37e445ece43d46a33ffe290 Mon Sep 17 00:00:00 2001 From: Stephan Troyer Date: Sun, 22 Mar 2020 15:16:31 +0100 Subject: [PATCH 1/7] add conversion JSON -> JS --- src/index.js | 28 +++++++++++++++++++++++++++- test/integration.test.js | 7 +++---- test/unit.test.js | 11 +++++++---- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/index.js b/src/index.js index 847fd15..441f291 100644 --- a/src/index.js +++ b/src/index.js @@ -104,6 +104,9 @@ const errorSignature = err => const getErrorCode = _.pipe(_.get('ruleId'), _.split('/'), _.last); +const preprocessorPlaceholder = '___'; +const preprocessorTemplate = `JSON.stringify(${preprocessorPlaceholder})`; + const processors = { '.json': { preprocess: function(text, fileName) { @@ -112,7 +115,15 @@ const processors = { const parsed = jsonServiceHandle.parseJSONDocument(textDocument); fileLintResults[fileName] = getDiagnostics(parsed); fileComments[fileName] = parsed.comments; - return ['']; // sorry nothing ;) + + let lastLineEnding = ''; + ['\n', '\r'].forEach(char => { + if (text.endsWith(char)) { + lastLineEnding = char + lastLineEnding; + text = text.slice(0, -1); + } + }); + return [preprocessorTemplate.replace(preprocessorPlaceholder, text) + lastLineEnding]; }, postprocess: function(messages, fileName) { const textDocument = fileDocuments[fileName]; @@ -144,6 +155,21 @@ const processors = { endColumn: error.endColumn + 1 }); }), + _.mapValues(error => { + const prefixLength = preprocessorTemplate.indexOf(preprocessorPlaceholder); + + let fix; + if (error.fix) { + fix = _.assign(error.fix, { + range: error.fix.range.map(location => location - prefixLength) + }); + } + return _.assign(error, { + column: error.column - (error.line === 1 ? prefixLength : 0), + endColumn: error.endColumn - (error.endLine === 1 ? prefixLength : 0), + fix + }); + }), _.values )(messages); } diff --git a/test/integration.test.js b/test/integration.test.js index f3ffdb7..40f1da7 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -45,10 +45,9 @@ function validateInfringementExpectation(expected, actualSituation) { else expect(actualSituation).to.have.property(scoped(rule)); } const allExpectedErrors = expected.map(_.pipe(_.split(':'), _.head, scoped)); - expect(_.xor(_.keys(actualSituation), allExpectedErrors)).to.have.length( - 0, - 'Extra errors found' - ); + // only check for errors generated by this plugin + const actualErrors = _.keys(actualSituation).filter(error => error.startsWith(scoped(''))); + expect(_.xor(actualErrors, allExpectedErrors)).to.have.length(0, 'Extra errors found'); } function validateFile(filename, config = {}) { diff --git a/test/unit.test.js b/test/unit.test.js index cfaf77a..c0cb72a 100644 --- a/test/unit.test.js +++ b/test/unit.test.js @@ -27,12 +27,13 @@ describe('plugin', function() { }); describe('preprocess', function() { const preprocess = plugin.processors['.json'].preprocess; - it('should return the same text', function() { + it('should contain the text', function() { const fileName = 'whatever-the-name.js'; - const newText = preprocess('whatever', fileName); + const text = 'whatever'; + const newText = preprocess(text, fileName); assert.isArray(newText, 'preprocess should return array'); - assert.strictEqual(newText[0], ''); + assert.include(newText[0], text); }); }); describe('postprocess', function() { @@ -92,7 +93,9 @@ describe('plugin', function() { const rules = ['undefined', 'trailing-comma']; const lintFile = fakeApplyRule(rules.map(rule => plugin.rules[rule])); const samples = [singleQuotes, trailingCommas, multipleErrors, trailingText, good]; - samples.forEach(sample => preprocess(sample.text, sample.fileName)); + samples.forEach(sample => { + sample.text = preprocess(sample.text, sample.fileName)[0]; + }); const errorsByFile = _.fromPairs( samples.map(sample => [sample.fileName, lintFile(sample.fileName)]) From b80d095a89fb3607bb005727457ee4983a891300 Mon Sep 17 00:00:00 2001 From: AdrieanKhisbe Date: Mon, 23 Mar 2020 23:28:34 +0100 Subject: [PATCH 2/7] Tweak implemntation of eslint-prettier-compatibility #38 by @stephtr --- src/index.js | 28 +++++++++++++--------------- test/unit.test.js | 10 ++++++---- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/index.js b/src/index.js index 441f291..a40e20e 100644 --- a/src/index.js +++ b/src/index.js @@ -116,14 +116,13 @@ const processors = { fileLintResults[fileName] = getDiagnostics(parsed); fileComments[fileName] = parsed.comments; - let lastLineEnding = ''; - ['\n', '\r'].forEach(char => { - if (text.endsWith(char)) { - lastLineEnding = char + lastLineEnding; - text = text.slice(0, -1); - } - }); - return [preprocessorTemplate.replace(preprocessorPlaceholder, text) + lastLineEnding]; + const [, eol = ''] = text.match(/([\n\r]{0,2})?$/); + return [ + preprocessorTemplate.replace( + preprocessorPlaceholder, + text.slice(0, text.length - eol.length) + ) + eol + ]; }, postprocess: function(messages, fileName) { const textDocument = fileDocuments[fileName]; @@ -156,18 +155,17 @@ const processors = { }); }), _.mapValues(error => { + if (_.startsWith('json/', error.ruleId)) return error; const prefixLength = preprocessorTemplate.indexOf(preprocessorPlaceholder); - let fix; - if (error.fix) { - fix = _.assign(error.fix, { - range: error.fix.range.map(location => location - prefixLength) - }); - } return _.assign(error, { column: error.column - (error.line === 1 ? prefixLength : 0), endColumn: error.endColumn - (error.endLine === 1 ? prefixLength : 0), - fix + fix: + error.fix && + _.assign(error.fix, { + range: error.fix.range.map(location => location - prefixLength) + }) }); }), _.values diff --git a/test/unit.test.js b/test/unit.test.js index c0cb72a..6831fea 100644 --- a/test/unit.test.js +++ b/test/unit.test.js @@ -92,10 +92,12 @@ describe('plugin', function() { const rules = ['undefined', 'trailing-comma']; const lintFile = fakeApplyRule(rules.map(rule => plugin.rules[rule])); - const samples = [singleQuotes, trailingCommas, multipleErrors, trailingText, good]; - samples.forEach(sample => { - sample.text = preprocess(sample.text, sample.fileName)[0]; - }); + let samples = [singleQuotes, trailingCommas, multipleErrors, trailingText, good].map( + sample => { + // !FIXME: check + return _.set('text', preprocess(sample.text, sample.fileName)[0], sample); + } + ); const errorsByFile = _.fromPairs( samples.map(sample => [sample.fileName, lintFile(sample.fileName)]) From 56efe6e45912e27efe637f2c323feec5d61b7bc9 Mon Sep 17 00:00:00 2001 From: Stephan Troyer Date: Tue, 24 Mar 2020 21:42:35 +0100 Subject: [PATCH 3/7] change eol regex --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index a40e20e..4ebab74 100644 --- a/src/index.js +++ b/src/index.js @@ -116,7 +116,7 @@ const processors = { fileLintResults[fileName] = getDiagnostics(parsed); fileComments[fileName] = parsed.comments; - const [, eol = ''] = text.match(/([\n\r]{0,2})?$/); + const [, eol = ''] = text.match(/([\n\r]*)$/); return [ preprocessorTemplate.replace( preprocessorPlaceholder, From 7baf12297f0e6ddbdd658555abfcced8a0b4661c Mon Sep 17 00:00:00 2001 From: Stephan Troyer Date: Wed, 25 Mar 2020 00:05:03 +0100 Subject: [PATCH 4/7] fix fixmapping of suffix --- src/index.js | 50 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/src/index.js b/src/index.js index 4ebab74..4e0767b 100644 --- a/src/index.js +++ b/src/index.js @@ -30,6 +30,7 @@ const AllowComments = 'allowComments'; const fileLintResults = {}; const fileComments = {}; const fileDocuments = {}; +const fileLengths = {}; const getSignature = problem => `${problem.range.start.line} ${problem.range.start.character} ${problem.message}`; @@ -107,6 +108,27 @@ const getErrorCode = _.pipe(_.get('ruleId'), _.split('/'), _.last); const preprocessorPlaceholder = '___'; const preprocessorTemplate = `JSON.stringify(${preprocessorPlaceholder})`; +function mapFix(fix, fileLength, prefixLength, suffix) { + let text = fix.text; + // We have to map the fix in such a way, that we account for the removed prefix and suffix. + let range = fix.range.map(location => location - prefixLength); + // For the suffix we have three cases: + // 1) The fix ends before the suffix => nothing left to do + if (range[0] >= fileLength + suffix.length) { + // 2) The fix starts after the suffix (for example concerning the last line break) + range = range.map(location => location - suffix.length); + } else if (range[1] >= fileLength) { + // 3) The fix intersects the suffix + range[1] = Math.max(range[1] - suffix.length, fileLength); + // in that case we have to delete the suffix also from the fix text. + const suffixPosition = text.lastIndexOf(suffix); + if (suffixPosition >= 0) { + text = text.slice(0, suffixPosition) + text.slice(suffixPosition + suffix.length); + } + } + return {range, text}; +} + const processors = { '.json': { preprocess: function(text, fileName) { @@ -117,17 +139,25 @@ const processors = { fileComments[fileName] = parsed.comments; const [, eol = ''] = text.match(/([\n\r]*)$/); + fileLengths[fileName] = text.length - eol.length; return [ preprocessorTemplate.replace( preprocessorPlaceholder, - text.slice(0, text.length - eol.length) + text.slice(0, fileLengths[fileName]) ) + eol ]; }, postprocess: function(messages, fileName) { const textDocument = fileDocuments[fileName]; + const fileLength = fileLengths[fileName]; delete fileLintResults[fileName]; delete fileComments[fileName]; + + const prefixLength = preprocessorTemplate.indexOf(preprocessorPlaceholder); + const suffix = preprocessorTemplate.slice( + prefixLength + preprocessorPlaceholder.length + ); + return _.pipe( _.first, _.groupBy(errorSignature), @@ -156,21 +186,21 @@ const processors = { }), _.mapValues(error => { if (_.startsWith('json/', error.ruleId)) return error; - const prefixLength = preprocessorTemplate.indexOf(preprocessorPlaceholder); - return _.assign(error, { + const newError = _.assign(error, { column: error.column - (error.line === 1 ? prefixLength : 0), - endColumn: error.endColumn - (error.endLine === 1 ? prefixLength : 0), - fix: - error.fix && - _.assign(error.fix, { - range: error.fix.range.map(location => location - prefixLength) - }) + endColumn: error.endColumn - (error.endLine === 1 ? prefixLength : 0) }); + if (error.fix) { + newError.fix = mapFix(error.fix, fileLength, prefixLength, suffix); + } + + return newError; }), _.values )(messages); - } + }, + supportsAutofix: true } }; From de35bb0c6e8ee2a21dfe482f7d9e751b3ce046f8 Mon Sep 17 00:00:00 2001 From: Stephan Troyer Date: Wed, 25 Mar 2020 00:11:28 +0100 Subject: [PATCH 5/7] add prettier fixes test --- examples/samples/unformatted.json | 3 +++ test/.eslintrc.with-prettier.json | 12 ++++++++++++ test/integration.test.js | 29 +++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 examples/samples/unformatted.json create mode 100644 test/.eslintrc.with-prettier.json diff --git a/examples/samples/unformatted.json b/examples/samples/unformatted.json new file mode 100644 index 0000000..2bc8f58 --- /dev/null +++ b/examples/samples/unformatted.json @@ -0,0 +1,3 @@ + { + "hello": "world" + } \ No newline at end of file diff --git a/test/.eslintrc.with-prettier.json b/test/.eslintrc.with-prettier.json new file mode 100644 index 0000000..c3e358d --- /dev/null +++ b/test/.eslintrc.with-prettier.json @@ -0,0 +1,12 @@ +{ + "plugins": ["self", "prettier"], + "rules": { + "prettier/prettier": ["error", { + "tabWidth": 4, + "useTabs": true, + "trailingComma": "all", + "singleQuote": true, + "endOfLine": "lf" + }] + } +} \ No newline at end of file diff --git a/test/integration.test.js b/test/integration.test.js index 40f1da7..37f1728 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -5,11 +5,18 @@ const _ = require('lodash/fp'); const SCOPE = 'self'; // (for test purpose only, relying the the eslint-plugin-self for tests) const scoped = rule => `${SCOPE}/${rule}`; -function getLintResults(filename, eslintConfig) { +function getLintResults(filename, eslintConfig, commands = []) { try { const results = execFileSync( 'eslint', - ['--config', eslintConfig || 'custom.eslintrc.json', '--format', 'json', filename], + [ + '--config', + eslintConfig || 'custom.eslintrc.json', + ...commands, + '--format', + 'json', + filename + ], { encoding: 'utf8', stdio: 'pipe', @@ -62,6 +69,15 @@ function validateFile(filename, config = {}) { expect(results.warningCount).to.equal(config.warningCount, 'invalid counr of warnings'); } +function validateFixes(filename, config = {}) { + const result = getLintResults(`samples/${filename}.json`, config.eslintrc, ['--fix-dry-run']); + + expect(result.output).not.to.be.undefined; + if (config.fixedOutput !== undefined) { + expect(result.output).to.equal(config.fixedOutput); + } +} + describe('Integrations tests', function() { it('validate correct json', function() { validateFile('good-json', {errorCount: 0, warningCount: 0}); @@ -118,3 +134,12 @@ describe('Integrations tests with config', function() { }); }); }); + +describe('Integrations tests with Prettier', function() { + it('prettifies JSON file', function() { + validateFixes('unformatted', { + eslintrc: '.eslintrc.with-prettier.json', + fixedOutput: '{\n\t"hello": "world"\n}\n' + }); + }); +}); From 72e2bdd19d4dd36a149833187f3831f2b0e8e97d Mon Sep 17 00:00:00 2001 From: Stephan Troyer Date: Wed, 25 Mar 2020 00:33:58 +0100 Subject: [PATCH 6/7] revert unnecessary test change --- test/unit.test.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/unit.test.js b/test/unit.test.js index 6831fea..a72de85 100644 --- a/test/unit.test.js +++ b/test/unit.test.js @@ -92,12 +92,8 @@ describe('plugin', function() { const rules = ['undefined', 'trailing-comma']; const lintFile = fakeApplyRule(rules.map(rule => plugin.rules[rule])); - let samples = [singleQuotes, trailingCommas, multipleErrors, trailingText, good].map( - sample => { - // !FIXME: check - return _.set('text', preprocess(sample.text, sample.fileName)[0], sample); - } - ); + const samples = [singleQuotes, trailingCommas, multipleErrors, trailingText, good]; + samples.forEach(sample => preprocess(sample.text, sample.fileName)); const errorsByFile = _.fromPairs( samples.map(sample => [sample.fileName, lintFile(sample.fileName)]) From 889bd75415fd5d8892e2aa6947f9e9d9180462a6 Mon Sep 17 00:00:00 2001 From: Stephan Troyer Date: Sun, 6 Dec 2020 00:18:25 +0100 Subject: [PATCH 7/7] fix prettier package.json compatibility --- src/index.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 4e0767b..f4343ba 100644 --- a/src/index.js +++ b/src/index.js @@ -141,10 +141,14 @@ const processors = { const [, eol = ''] = text.match(/([\n\r]*)$/); fileLengths[fileName] = text.length - eol.length; return [ - preprocessorTemplate.replace( - preprocessorPlaceholder, - text.slice(0, fileLengths[fileName]) - ) + eol + { + text: + preprocessorTemplate.replace( + preprocessorPlaceholder, + text.slice(0, fileLengths[fileName]) + ) + eol, + filename: 'extracted.json' + } ]; }, postprocess: function(messages, fileName) {