├── .gitignore ├── .vscodeignore ├── CHANGELOG.md ├── README.md ├── package.json ├── prettier-logo.png ├── screenshots ├── command.png └── format.png ├── src ├── PrettierESLintEditProvider.ts ├── commands.ts ├── extension.ts └── utils.ts ├── test ├── extension.test.ts └── index.ts ├── tsconfig.json └── vsc-extension-quickstart.md /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | tsconfig.json 9 | vsc-extension-quickstart.md 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to the "prettier-eslint" extension will be documented in this file. 3 | 4 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 5 | 6 | ## [0.7.0] 7 | 8 | - Add all prettier options. 9 | 10 | ## [0.6.0] 11 | 12 | - Update to latest [prettier-eslint](https://github.com/prettier/prettier-eslint) version 5.1.0. 13 | 14 | ## [0.5.0] 15 | 16 | - You can specify a custom `prettierPath` via `prettier-eslint.prettierPath`. 17 | 18 | ## [0.4.0] 19 | 20 | - You can add a `prettier.printWidth` option. 21 | - You can specify a custom `eslintPath` via `prettier-eslint.eslintPath`. 22 | - There is a default keybinding now `Cmd+Alt+P` which can be edited ofcourse. 23 | 24 | ## [0.3.0] 25 | 26 | - The status bar output is now clickable and it opens an output window with more information. 27 | 28 | ## [0.2.0] 29 | 30 | - It formats the code when you save the file, only if you have `editor.formatOnSave` set to true in settings 31 | 32 | ## [0.1.0] 33 | 34 | - Initial release -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | Please use the following package: [prettier-vscode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) (`ext install prettier-vscode`). 4 | This package is deprecated and won't be maintained anymore. 5 | 6 | # Prettier-ESLint for Visual Studio Code 7 | 8 | Format your javascript via Prettier and ESLint --fix. This uses [`prettier-eslint`][prettier-eslint] to automatically format and fix on save. 9 | 10 | ## Usage 11 | 12 | - You can manually run the command: ![](https://github.com/RobinMalfait/prettier-eslint-code/raw/master/screenshots/command.png) 13 | - You can use the build in formatter: ![](https://github.com/RobinMalfait/prettier-eslint-code/raw/master/screenshots/format.png) 14 | 15 | When you enable the `editor.formatOnSave` config, it works out of the box! 16 | 17 | ## Settings 18 | 19 | - `prettier.printWidth` can be used to set a custom print width. 20 | - `prettier-eslint.eslintPath` can be used to set a custom eslint path. 21 | 22 | ## Prettier - Settings 23 | 24 | ```js 25 | { 26 | // Indent lines with tabs 27 | "prettier.useTabs": false, 28 | 29 | // Fit code within this line limit 30 | "prettier.printWidth": 80, 31 | 32 | // Number of spaces it should use per tab 33 | "prettier.tabWidth": 2, 34 | 35 | // If true, will use single instead of double quotes 36 | "prettier.singleQuote": false, 37 | 38 | // Controls the printing of trailing commas wherever possible. Valid options: 39 | // "none" - No trailing commas 40 | // "es5" - Trailing commas where valid in ES5 (objects, arrays, etc) 41 | // "all" - Trailing commas wherever possible (function arguments) 42 | "prettier.trailingComma": "none", 43 | 44 | // Controls the printing of spaces inside object literals 45 | "prettier.bracketSpacing": true, 46 | 47 | // If true, puts the `>` of a multi-line jsx element at the end of 48 | // the last line instead of being alone on the next line 49 | "prettier.jsxBracketSameLine": false, 50 | 51 | // Which parser to use. Valid options are 'flow' and 'babylon' 52 | "prettier.parser": "babylon", 53 | 54 | // Whether to add a semicolon at the end of every line (semi: true), 55 | // or only at the beginning of lines that may introduce ASI failures (semi: false) 56 | "prettier.semi": true, 57 | } 58 | ``` 59 | 60 | ### Installation 61 | 62 | Install through VS Code extensions. Search for `Prettier - ESLint` 63 | 64 | [Visual Studio Code Market Place: Prettier - ESLint](https://marketplace.visualstudio.com/items?itemName=RobinMalfait.prettier-eslint-vscode) 65 | 66 | Can also be installed using 67 | 68 | ``` 69 | ext install prettier-eslint-vscode 70 | ``` 71 | 72 | ## Known Issues 73 | 74 | / 75 | 76 | ## Release Notes 77 | 78 | Take a look at our [CHANGELOG](CHANGELOG.md) 79 | 80 | [prettier-eslint]: https://github.com/prettier/prettier-eslint -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prettier-eslint-vscode", 3 | "displayName": "Prettier - ESLint", 4 | "description": "Code extensions to format your JavaScript using Prettier and ESLint (with eslint --fix)", 5 | "version": "0.7.3", 6 | "publisher": "RobinMalfait", 7 | "icon": "prettier-logo.png", 8 | "homepage": "https://marketplace.visualstudio.com/items?itemName=RobinMalfait.prettier-eslint-vscode", 9 | "repository": { 10 | "type": "git", 11 | "url": "http://github.com/RobinMalfait/prettier-eslint-code.git" 12 | }, 13 | "license": "MIT", 14 | "bugs": { 15 | "url": "http://github.com/RobinMalfait/prettier-eslint-code/issues" 16 | }, 17 | "engines": { 18 | "vscode": "^1.5.0" 19 | }, 20 | "categories": [ 21 | "Formatters" 22 | ], 23 | "activationEvents": [ 24 | "onCommand:prettier-eslint.format", 25 | "prettier-eslint.open-output", 26 | "workspaceContains:.eslintrc", 27 | "workspaceContains:.eslintrc.js", 28 | "workspaceContains:.eslintrc.json", 29 | "workspaceContains:.eslintrc.yaml", 30 | "workspaceContains:.eslintrc.yml", 31 | "onLanguage:javascript", 32 | "onLanguage:javascriptreact" 33 | ], 34 | "main": "./out/src/extension", 35 | "contributes": { 36 | "commands": [ 37 | { 38 | "command": "prettier-eslint.format", 39 | "title": "Prettier ESLint: format" 40 | } 41 | ], 42 | "configuration": { 43 | "type": "object", 44 | "title": "Prettier-eslint", 45 | "properties": { 46 | "prettier.useTabs": { 47 | "type": "boolean", 48 | "default": false, 49 | "description": "Indent lines with tabs" 50 | }, 51 | "prettier.printWidth": { 52 | "type": "integer", 53 | "default": 80, 54 | "description": "Fit code within this line limit" 55 | }, 56 | "prettier.tabWidth": { 57 | "type": "integer", 58 | "default": 2, 59 | "description": "Number of spaces it should use per tab" 60 | }, 61 | "prettier.singleQuote": { 62 | "type": "boolean", 63 | "default": false, 64 | "description": "If true, will use single instead of double quotes" 65 | }, 66 | "prettier.trailingComma": { 67 | "type": "string", 68 | "default": "none", 69 | "enum": [ 70 | "none", 71 | "es5", 72 | "all" 73 | ], 74 | "description": "Controls the printing of trailing commas wherever possible. Valid options:\n\"none\" - No trailing commas\n\"es5\" - Trailing commas where valid in ES5 (objects, arrays, etc)\n\"all\" - Trailing commas wherever possible (function arguments)" 75 | }, 76 | "prettier.bracketSpacing": { 77 | "type": "boolean", 78 | "default": true, 79 | "description": "Controls the printing of spaces inside object literals" 80 | }, 81 | "prettier.jsxBracketSameLine": { 82 | "type": "boolean", 83 | "default": false, 84 | "description": "If true, puts the `>` of a multi-line jsx element at the end of\nthe last line instead of being alone on the next line" 85 | }, 86 | "prettier.parser": { 87 | "type": "string", 88 | "default": "babylon", 89 | "enum": [ 90 | "babylon", 91 | "flow" 92 | ], 93 | "description": "Which parser to use. Valid options are 'flow' and 'babylon'" 94 | }, 95 | "prettier.semi": { 96 | "type": "boolean", 97 | "default": true, 98 | "description": "Whether to add a semicolon at the end of every line (semi: true),\nor only at the beginning of lines that may introduce ASI failures (semi: false)" 99 | }, 100 | "prettier-eslint.eslintPath": { 101 | "type": "string", 102 | "default": "", 103 | "description": "An optional path to eslint, otherwise it will try to guess the path." 104 | }, 105 | "prettier-eslint.prettierPath": { 106 | "type": "string", 107 | "default": "", 108 | "description": "An optional path to prettier, otherwise it will try to guess the path." 109 | } 110 | } 111 | }, 112 | "keybindings": [ 113 | { 114 | "command": "prettier-eslint.format", 115 | "key": "Cmd+Alt+P" 116 | } 117 | ] 118 | }, 119 | "scripts": { 120 | "vscode:prepublish": "tsc -p ./", 121 | "compile": "tsc -watch -p ./", 122 | "postinstall": "node ./node_modules/vscode/bin/install", 123 | "test": "node ./node_modules/vscode/bin/test" 124 | }, 125 | "devDependencies": { 126 | "@types/mocha": "^2.2.41", 127 | "@types/node": "^8.0.7", 128 | "mocha": "^3.4.2", 129 | "typescript": "^2.4.1", 130 | "vscode": "^1.1.2" 131 | }, 132 | "dependencies": { 133 | "prettier-eslint": "^6.4.1", 134 | "require-relative": "^0.8.7" 135 | } 136 | } -------------------------------------------------------------------------------- /prettier-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobinMalfait/prettier-eslint-code/7c92aebd0a3618224370f65d03416b18119670d9/prettier-logo.png -------------------------------------------------------------------------------- /screenshots/command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobinMalfait/prettier-eslint-code/7c92aebd0a3618224370f65d03416b18119670d9/screenshots/command.png -------------------------------------------------------------------------------- /screenshots/format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobinMalfait/prettier-eslint-code/7c92aebd0a3618224370f65d03416b18119670d9/screenshots/format.png -------------------------------------------------------------------------------- /src/PrettierESLintEditProvider.ts: -------------------------------------------------------------------------------- 1 | import { 2 | workspace, 3 | DocumentRangeFormattingEditProvider, 4 | DocumentFormattingEditProvider, 5 | Range, 6 | TextDocument, 7 | FormattingOptions, 8 | CancellationToken, 9 | TextEdit, 10 | window 11 | } from 'vscode'; 12 | 13 | import { 14 | fullDocumentRange, 15 | format 16 | } from './utils'; 17 | 18 | class PrettierEditProvider implements DocumentRangeFormattingEditProvider, DocumentFormattingEditProvider { 19 | provideDocumentRangeFormattingEdits( 20 | document: TextDocument, 21 | range: Range, 22 | options: FormattingOptions, 23 | token: CancellationToken 24 | ): TextEdit[] { 25 | return [TextEdit.replace(range, format(document.getText(range), document.fileName))]; 26 | } 27 | 28 | provideDocumentFormattingEdits( 29 | document: TextDocument, 30 | options: FormattingOptions, 31 | token: CancellationToken 32 | ): TextEdit[] { 33 | return [TextEdit.replace(fullDocumentRange(document), format(document.getText(), document.fileName))]; 34 | } 35 | } 36 | 37 | export default PrettierEditProvider; -------------------------------------------------------------------------------- /src/commands.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TextDocument, 3 | commands, 4 | window 5 | } from 'vscode'; 6 | 7 | import { formatDocument, registerOutputHandler } from './utils'; 8 | 9 | export const registerPrettierESLintCommand = (validLanguages) => { 10 | return commands.registerCommand('prettier-eslint.format', () => { 11 | formatDocument( 12 | validLanguages, 13 | window.activeTextEditor.document, 14 | window.activeTextEditor 15 | ); 16 | }); 17 | } 18 | 19 | export const registerPrettierESLintCommandOutput = () => { 20 | const outputChannel = window.createOutputChannel('Prettier ESLint'); 21 | 22 | registerOutputHandler((output) => { 23 | outputChannel.clear(); 24 | outputChannel.append(output); 25 | }); 26 | 27 | return commands.registerCommand('prettier-eslint.open-output', () => { 28 | outputChannel.show(); 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { 4 | languages, 5 | ExtensionContext, 6 | DocumentSelector, 7 | window, 8 | workspace, 9 | } from 'vscode'; 10 | import { 11 | registerPrettierESLintCommand, 12 | registerPrettierESLintCommandOutput 13 | } from './commands'; 14 | import EditProvider from './PrettierESLintEditProvider'; 15 | 16 | const VALID_LANG: DocumentSelector = ['javascript', 'javascriptreact']; 17 | 18 | export function activate(context: ExtensionContext) { 19 | const editProvider = new EditProvider() 20 | 21 | const disposables = [ 22 | // Register all content providers 23 | languages.registerDocumentRangeFormattingEditProvider(VALID_LANG, editProvider), 24 | languages.registerDocumentFormattingEditProvider(VALID_LANG, editProvider), 25 | 26 | // Register all commands 27 | registerPrettierESLintCommand(VALID_LANG), 28 | registerPrettierESLintCommandOutput(), 29 | ]; 30 | 31 | context.subscriptions.push(...disposables); 32 | } 33 | 34 | export function deactivate() { 35 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TextDocument, 3 | Range, 4 | Selection, 5 | TextEditor, 6 | StatusBarItem, 7 | window, 8 | workspace 9 | } from 'vscode'; 10 | 11 | const requireRelative = require('require-relative'); 12 | 13 | let statusbar: StatusBarItem = undefined; 14 | let outputHandler: Function = () => { }; 15 | 16 | function showStatusBarMessage(message, output) { 17 | if (statusbar === undefined) { 18 | statusbar = window.createStatusBarItem(); 19 | } 20 | 21 | outputHandler(output); 22 | 23 | statusbar.text = message; 24 | statusbar.command = 'prettier-eslint.open-output'; 25 | statusbar.show(); 26 | } 27 | 28 | export function registerOutputHandler(handler: Function = () => { }) { 29 | outputHandler = handler; 30 | } 31 | 32 | export function fullDocumentRange(document: TextDocument): Range { 33 | const lastLineId = document.lineCount - 1; 34 | return new Range(0, 0, lastLineId, document.lineAt(lastLineId).text.length); 35 | } 36 | 37 | export function fullDocumentSelection(document: TextDocument): Selection { 38 | return fullDocumentRange(document) as Selection; 39 | } 40 | 41 | const getPath = (path) => { 42 | const trimmedPath = path.trim(); 43 | 44 | return trimmedPath === "" 45 | ? undefined 46 | : trimmedPath; 47 | } 48 | 49 | export function format(text: string = '', filePath: string) { 50 | try { 51 | const formatPrettierESLint = requirePrettierEslint(filePath); 52 | const prettierOptions = workspace.getConfiguration('prettier') as any; 53 | const prettierEslintOptions = workspace.getConfiguration('prettier-eslint') as any; 54 | 55 | const formattedOutput = formatPrettierESLint({ 56 | text, 57 | filePath, 58 | eslintPath: getPath(prettierEslintOptions.eslintPath), 59 | prettierPath: getPath(prettierEslintOptions.prettierPath), 60 | prettierOptions 61 | }); 62 | 63 | showStatusBarMessage('Prettier ESLint: $(check)', 'All good!'); 64 | 65 | return formattedOutput; 66 | } catch (err) { 67 | showStatusBarMessage('Prettier ESLint: $(x)', err.toString()); 68 | return text; 69 | } 70 | } 71 | 72 | export function formatDocument(validLanguages: any, document: TextDocument, editor: TextEditor): Thenable { 73 | if (!validLanguages.includes(document.languageId)) { 74 | return Promise.reject('Language is not valid'); 75 | } 76 | 77 | const { selections } = editor; 78 | 79 | const selectionsToBeReplaced = selections.filter((selection) => { 80 | return !(selection.start.line === selection.end.line 81 | && selection.start.character === selection.end.character); 82 | }); 83 | const hasSelections = selectionsToBeReplaced.length > 0; 84 | 85 | if (!hasSelections) { 86 | selectionsToBeReplaced.push(fullDocumentSelection(document)); 87 | } 88 | 89 | return Promise.all(selectionsToBeReplaced.map((selection) => { 90 | // Replace selection with new, formatted text 91 | return editor 92 | .edit((editBuilder) => editBuilder.replace( 93 | selection, 94 | format(document.getText(selection), document.fileName) 95 | )); 96 | })); 97 | } 98 | 99 | function requirePrettierEslint(filePath: string) { 100 | try { 101 | return requireRelative('prettier-eslint', filePath); 102 | } catch (err) { 103 | return require('prettier-eslint'); 104 | } 105 | } -------------------------------------------------------------------------------- /test/extension.test.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Note: This example test is leveraging the Mocha test framework. 3 | // Please refer to their documentation on https://mochajs.org/ for help. 4 | // 5 | 6 | // The module 'assert' provides assertion methods from node 7 | import * as assert from 'assert'; 8 | 9 | // You can import and use all API from the 'vscode' module 10 | // as well as import your extension to test it 11 | import * as vscode from 'vscode'; 12 | import * as myExtension from '../src/extension'; 13 | 14 | // Defines a Mocha test suite to group tests of similar kind together 15 | suite("Extension Tests", () => { 16 | 17 | // Defines a Mocha unit test 18 | test("Something 1", () => { 19 | assert.equal(-1, [1, 2, 3].indexOf(5)); 20 | assert.equal(-1, [1, 2, 3].indexOf(0)); 21 | }); 22 | }); -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | var testRunner = require('vscode/lib/testrunner'); 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 19 | useColors: true // colored output from test results 20 | }); 21 | 22 | module.exports = testRunner; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "." 11 | }, 12 | "exclude": [ 13 | "node_modules", 14 | ".vscode-test" 15 | ] 16 | } -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your first VS Code Extension 2 | 3 | ## What's in the folder 4 | * This folder contains all of the files necessary for your extension 5 | * `package.json` - this is the manifest file in which you declare your extension and command. 6 | The sample plugin registers a command and defines its title and command name. With this information 7 | VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command. 9 | The file exports one function, `activate`, which is called the very first time your extension is 10 | activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 11 | We pass the function containing the implementation of the command as the second parameter to 12 | `registerCommand`. 13 | 14 | ## Get up and running straight away 15 | * press `F5` to open a new window with your extension loaded 16 | * run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World` 17 | * set breakpoints in your code inside `src/extension.ts` to debug your extension 18 | * find output from your extension in the debug console 19 | 20 | ## Make changes 21 | * you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts` 22 | * you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes 23 | 24 | ## Explore the API 25 | * you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts` 26 | 27 | ## Run tests 28 | * open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests` 29 | * press `F5` to run the tests in a new window with your extension loaded 30 | * see the output of the test result in the debug console 31 | * make changes to `test/extension.test.ts` or create new test files inside the `test` folder 32 | * by convention, the test runner will only consider files matching the name pattern `**.test.ts` 33 | * you can create folders inside the `test` folder to structure your tests any way you want --------------------------------------------------------------------------------