diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8c52ff9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76b1021 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.nyc_output +coverage +node_modules diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2de7d16 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +dist: trusty +branches: + except: /^v\d/ +language: node_js +node_js: node +after_script: + - npm install coveralls + - nyc report --reporter=text-lcov | coveralls diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0b0cb16 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017 Shinnosuke Watanabe + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..788311a --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +# postcss-error-to-vscode-diagnostic + +[![NPM version](https://img.shields.io/npm/v/postcss-error-to-vscode-diagnostic.svg)](https://www.npmjs.com/package/postcss-error-to-vscode-diagnostic) +[![Build Status](https://travis-ci.org/shinnn/postcss-error-to-vscode-diagnostic.svg?branch=master)](https://travis-ci.org/shinnn/postcss-error-to-vscode-diagnostic) +[![Coverage Status](https://img.shields.io/coveralls/shinnn/postcss-error-to-vscode-diagnostic.svg)](https://coveralls.io/github/shinnn/postcss-error-to-vscode-diagnostic?branch=master) + +Convert a [`CssSyntaxError`](http://api.postcss.org/CssSyntaxError.html) of [PostCSS](http://postcss.org/) into a [diagnostic](https://code.visualstudio.com/docs/extensionAPI/vscode-api#Diagnostic) of [Visual Studio Code](https://code.visualstudio.com/) + +```javascript +const postcss = require('postcss'); +const postcssErrorToVscodeDiagnostic = require('postcss-error-to-vscode-diagnostic'); + +const {error} = postcss().process(` + div { + color::red + } +`); +/* CssSyntaxError { + reason: 'Double colon', + line: 3, + column: 11, + ... +} */ + +stylelintWarningToVscodeDiagnostic(error); +/* { + message: 'Double colon (syntax error)', + severity: 1, + range: { + start: { + line: 2, + column: 10 + }, + end: { + line: 2, + column: 10 + } + } +} */ +``` + +## Installation + +[Use npm.](https://docs.npmjs.com/cli/install) + +``` +npm install postcss-error-to-vscode-diagnostic +``` + +## API + +```javascript +const postcssErrorToVscodeDiagnostic = require('postcss-error-to-vscode-diagnostic'); +``` + +### postcssErrorToVscodeDiagnostic(*error* [, *additionalProperties*]) + +*error*: [`CssSyntaxError`](https://github.com/postcss/postcss/blob/ea3290a31d28dbc1502e1f03fddb1c927dcdbc35/lib/css-syntax-error.es6#L34) +*additionalProperties*: `Object` +Return: `Object` (VS Code [diagnostic](https://github.com/Microsoft/vscode-languageserver-node/blob/release/3.0.3/types/src/main.ts#L161-L192)) + +The returned diagnostic has [`message`](https://github.com/Microsoft/vscode-languageserver-node/blob/2f9d6055a77d8e9d31ecda03f8b1c54dd5ea0246/types/src/main.ts#L188-L191), [`range`](https://github.com/Microsoft/vscode-languageserver-node/blob/2f9d6055a77d8e9d31ecda03f8b1c54dd5ea0246/types/src/main.ts#L166-L169) and [`severity`](https://github.com/Microsoft/vscode-languageserver-node/blob/2f9d6055a77d8e9d31ecda03f8b1c54dd5ea0246/types/src/main.ts#L171-L175) by default. + +All properties of the second argument will be [assigned](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to the return value. For example you can add a missing [`source`](https://github.com/Microsoft/vscode-languageserver-node/blob/2f9d6055a77d8e9d31ecda03f8b1c54dd5ea0246/types/src/main.ts#L182-L186) property as below: + +```javascript +const error = postcss().process('foo'); + +postcssErrorToVscodeDiagnostic(error); +/* { + message: 'Unknown word (syntax error)', + severity: 1, + range: { ... } +} */ + +postcssErrorToVscodeDiagnostic(error, {source: 'my-awesome-linter'}); +/* { + message: 'Unknown word (syntax error)', + severity: 1, + range: { ... }, + source: 'my-awesome-linter' +} */ +``` + +## License + +Copyright (c) 2017 [Shinnosuke Watanabe](https://github.com/shinnn) + +Licensed under [the MIT License](./LICENSE). diff --git a/index.js b/index.js new file mode 100644 index 0000000..0f25894 --- /dev/null +++ b/index.js @@ -0,0 +1,46 @@ +/*! + * postcss-error-to-vscode-diagnostic | MIT (c) Shinnosuke Watanabe + * https://github.com/shinnn/postcss-error-to-vscode-diagnostic +*/ +'use strict'; + +const {inspect} = require('util'); + +module.exports = function postcssErrorToVscodeDiagnostic(...args) { + const argLen = args.length; + + if (argLen !== 1 && argLen !== 2) { + throw new TypeError(`Expected 1 or 2 arguments (object[, object]), but got ${ + argLen === 0 ? 'no' : argLen + } arguments instead.`); + } + + const error = args[0]; + + if (error === null || typeof error !== 'object' || error.name !== 'CssSyntaxError') { + throw new TypeError(`Expected an instance of postcss's CssSyntaxError, but got ${ + inspect(error) + } instead. http://api.postcss.org/CssSyntaxError.html`); + } + + const position = { + line: error.line - 1, + column: error.column - 1 + }; + + const diagnostic = { + message: `${error.reason} (syntax error)`, + // https://github.com/Microsoft/vscode-languageserver-node/blob/release/3.0.3/types/src/main.ts#L144 + severity: 1, + range: { + start: position, + end: position + } + }; + + if (argLen === 1) { + return diagnostic; + } + + return Object.assign(diagnostic, args[1]); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..1a85765 --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "postcss-error-to-vscode-diagnostic", + "version": "1.0.0", + "description": "Convert a CssSyntaxError of PostCSS into a diagnostic of Visual Studio Code", + "repository": "shinnn/postcss-error-to-vscode-diagnostic", + "author": "Shinnosuke Watanabe (https://github.com/shinnn)", + "scripts": { + "pretest": "eslint --fix --format=codeframe index.js test.js", + "test": "nyc --reporter=html --reporter=text node test.js" + }, + "license": "MIT", + "files": [ + "index.js" + ], + "keywords": [ + "css", + "postcss", + "vscode", + "syntax", + "error", + "diagnostic", + "convert", + "compatibility", + "interface", + "object" + ], + "devDependencies": { + "@shinnn/eslint-config-node": "^3.0.0", + "eslint": "^3.15.0", + "nyc": "^10.1.2", + "postcss": "^5.2.14", + "tape": "^4.6.3" + }, + "eslintConfig": { + "extends": "@shinnn/node" + } +} diff --git a/test.js b/test.js new file mode 100644 index 0000000..537a9e5 --- /dev/null +++ b/test.js @@ -0,0 +1,78 @@ +'use strict'; + +const fn = require('.'); +const postcss = require('postcss'); +const test = require('tape'); + +test('postcssErrorToVscodeDiagnostic()', t => { + t.deepEqual( + fn(postcss().process('a').error), + { + message: 'Unknown word (syntax error)', + range: { + end: { + column: 0, + line: 0 + }, + start: { + column: 0, + line: 0 + } + }, + severity: 1 + }, + 'should convert a CssSyntaxError into a VS Code diagnostic.' + ); + + t.deepEqual( + fn(postcss().process('\n }').error, {source: 'awesome-css-processor'}), + { + message: 'Unexpected } (syntax error)', + range: { + end: { + line: 1, + column: 2 + }, + start: { + line: 1, + column: 2 + } + }, + severity: 1, + source: 'awesome-css-processor' + }, + 'should override values with the second argument.' + ); + + t.throws( + () => fn(), + /^TypeError.*Expected 1 or 2 arguments \(object\[, object]\), but got no arguments instead\./, + 'should throw an error when it takes no arguments.' + ); + + t.throws( + () => fn({}, {}, {}), + /^TypeError.*Expected 1 or 2 arguments \(object\[, object]\), but got 3 arguments instead\./, + 'should throw an error when it takes too many arguments.' + ); + + t.throws( + () => fn(null), + /^TypeError.*Expected an instance of postcss's CssSyntaxError, but got null instead\. /, + 'should throw an error when it takes null.' + ); + + t.throws( + () => fn(Symbol('Hi')), + /^TypeError.*Expected an instance of postcss's CssSyntaxError, but got Symbol\(Hi\) instead\. /, + 'should throw an error when it takes a non-object value.' + ); + + t.throws( + () => fn({foo: 'bar'}), + /^TypeError.*Expected an instance of postcss's CssSyntaxError, but got { foo: 'bar' } instead\. /, + 'should throw an error when the frist argument is not a CssSyntaxError.' + ); + + t.end(); +});