├── index.js ├── .prettierrc ├── .npmignore ├── __tests__ ├── utils │ └── check-output-loader.js └── index.js ├── CONTRIBUTING.md ├── .mergify.yml ├── .eslintrc.json ├── .travis.yml ├── ISSUE_TEMPLATE.md ├── LICENSE ├── .gitignore ├── package.json ├── CODE_OF_CONDUCT.md ├── prettier-loader.js └── README.md /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./prettier-loader'); 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | __tests__/ 2 | coverage/ 3 | .eslintrc.json 4 | .prettierrc 5 | .travis.yml 6 | CODE_OF_CONDUCT.md 7 | CONTRIBUTING.md 8 | ISSUE_TEMPLATE.md 9 | yarn.lock -------------------------------------------------------------------------------- /__tests__/utils/check-output-loader.js: -------------------------------------------------------------------------------- 1 | const loaderUtils = require('loader-utils'); 2 | 3 | module.exports = function(source) { 4 | const { checkResult } = loaderUtils.getOptions(this); 5 | checkResult(source); 6 | return source; 7 | }; 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | All pull requests that respect next rules are welcome: 4 | 5 | - Before opening pull request to this repo run `npm t` to lint, prettify and run test on code. 6 | 7 | - All bugfixes and features should contain tests. 8 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: merge pull requests from dependabot if CI passes 3 | conditions: 4 | - author=dependabot-preview[bot] 5 | - status-success=continuous-integration/travis-ci/pr 6 | actions: 7 | merge: 8 | method: merge 9 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["node"], 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:node/recommended", 6 | "plugin:prettier/recommended" 7 | ], 8 | "env": { 9 | "jest": true 10 | }, 11 | "rules": { 12 | "no-unused-vars": ["error", { "ignoreRestSiblings": true }] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - node_modules 5 | notifications: 6 | email: false 7 | node_js: 8 | - '8.10.0' 9 | script: 10 | - npm run test 11 | - | 12 | if [[ ( "$TRAVIS_BRANCH" == "master" ) && ( "$TRAVIS_PULL_REQUEST" == "false" ) ]]; then 13 | npm run report; 14 | fi 15 | before_install: 16 | - npm install -g yarn 17 | branches: 18 | except: 19 | - /^v\d+\.\d+\.\d+$/ 20 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | _If you know how to fix the issue, make a pull request instead._ 2 | 3 | - [ ] I tried using [the latest stable version of prettier-loader](https://www.npmjs.com/package/prettier-loader) 4 | - [ ] I've read [readme](https://github.com/iamolegga/prettier-loader/blob/master/README.md) 5 | 6 | _Select one of these and delete the others:_ 7 | 8 | _If asking a question:_ 9 | 10 | - [ ] Question: ... 11 | - [ ] Code (if necessary) below: 12 | ```js 13 | 14 | ``` 15 | 16 | _If reporting a bug:_ 17 | 18 | - [ ] Steps to repeat: 19 | - [ ] Code (if necessary) below: 20 | ```js 21 | 22 | ``` 23 | - [ ] Expected behavior: 24 | - [ ] Observed behavior: 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Oleg Repin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prettier-loader", 3 | "version": "3.3.0", 4 | "description": "webpack prettier loader", 5 | "main": "prettier-loader.js", 6 | "engines": { 7 | "node": ">=8.3.0" 8 | }, 9 | "scripts": { 10 | "lint": "eslint '**/*.js' --fix", 11 | "test": "npm run lint && jest", 12 | "report": "cat ./coverage/lcov.info | coveralls" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/iamolegga/prettier-loader.git" 17 | }, 18 | "keywords": [ 19 | "webpack", 20 | "prettier" 21 | ], 22 | "author": "Oleg Repin (http://github.com/iamolegga)", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/iamolegga/prettier-loader/issues" 26 | }, 27 | "homepage": "https://github.com/iamolegga/prettier-loader#readme", 28 | "dependencies": { 29 | "ignore": "^5.1.4", 30 | "loader-utils": "^2.0.0" 31 | }, 32 | "devDependencies": { 33 | "coveralls": "^3.0.6", 34 | "eslint": "^6.5.0", 35 | "eslint-config-prettier": "^6.3.0", 36 | "eslint-plugin-node": "^11.0.0", 37 | "eslint-plugin-prettier": "^3.1.1", 38 | "jest": "^24.9.0", 39 | "prettier": "^1.18.2", 40 | "rimraf": "^3.0.0", 41 | "webpack": "^4.41.0", 42 | "webpack2": "npm:webpack@2", 43 | "webpack3": "npm:webpack@3" 44 | }, 45 | "peerDependencies": { 46 | "prettier": ">=1.6.0", 47 | "webpack": ">=2.0.0" 48 | }, 49 | "jest": { 50 | "testEnvironment": "node", 51 | "collectCoverage": true, 52 | "testPathIgnorePatterns": [ 53 | "__tests__/utils" 54 | ], 55 | "collectCoverageFrom": [ 56 | "./prettier-loader.js" 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at iamolegga@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /prettier-loader.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const prettier = require('prettier'); 4 | const loaderUtils = require('loader-utils'); 5 | const ignore = require('ignore'); 6 | 7 | // prettier config 8 | 9 | const configsCache = {}; 10 | 11 | async function getConfig(filePath, loaderOptions) { 12 | if (configsCache[filePath]) { 13 | return configsCache[filePath]; 14 | } 15 | 16 | const { resolveConfigOptions, ...passedToLoaderPrettierOptions } = 17 | loaderOptions || {}; 18 | 19 | const outerOptions = await prettier.resolveConfig( 20 | filePath, 21 | resolveConfigOptions 22 | ); 23 | 24 | const mergedConfig = Object.assign( 25 | {}, 26 | outerOptions || {}, 27 | passedToLoaderPrettierOptions 28 | ); 29 | 30 | // eslint-disable-next-line require-atomic-updates 31 | configsCache[filePath] = mergedConfig; 32 | 33 | return mergedConfig; 34 | } 35 | 36 | // prettier ignore 37 | 38 | let ignoreManager; 39 | 40 | function getIgnoreManager(filePath) { 41 | if (ignoreManager) { 42 | return ignoreManager; 43 | } 44 | ignoreManager = ignore(); 45 | const ignorePath = findIgnorePathInParentFolders(path.join(filePath, '..')); 46 | if (ignorePath) { 47 | const ignoredFiles = fs.readFileSync(ignorePath, 'utf8').toString(); 48 | ignoreManager.add(ignoredFiles); 49 | } 50 | return ignoreManager; 51 | } 52 | function findIgnorePathInParentFolders(folderPath) { 53 | const possiblePath = path.join(`${folderPath}`, '.prettierignore'); 54 | if (fs.existsSync(possiblePath)) { 55 | return possiblePath; 56 | } 57 | const parentFolder = path.join(folderPath, '..'); 58 | if (parentFolder === folderPath) { 59 | return null; 60 | } 61 | return findIgnorePathInParentFolders(parentFolder); 62 | } 63 | 64 | // loader 65 | 66 | const loadedFiles = new Set(); 67 | 68 | module.exports = async function(source, map) { 69 | this.cacheable(); 70 | const callback = this.async(); 71 | 72 | if (!new RegExp(this.query.test).test(this.context)) { 73 | return callback(null, source, map); 74 | } 75 | 76 | if ( 77 | getIgnoreManager(this.resourcePath).ignores( 78 | // webpack4 specific `rootContext` property 79 | // against `options.context` in earlier versions 80 | path.relative(this.rootContext || this.options.context, this.resourcePath) 81 | ) 82 | ) { 83 | return callback(null, source, map); 84 | } 85 | 86 | const { skipRewritingSource, ignoreInitial, ...config } = await getConfig( 87 | this.resourcePath, 88 | loaderUtils.getOptions(this) 89 | ); 90 | 91 | if (!!ignoreInitial && !loadedFiles.has(this.resourcePath)) { 92 | loadedFiles.add(this.resourcePath); 93 | return callback(null, source, map); 94 | } 95 | 96 | let prettierSource; 97 | try { 98 | prettierSource = prettier.format(source, config); 99 | } catch (e) { 100 | return callback(e); 101 | } 102 | 103 | if (!skipRewritingSource && prettierSource !== source) { 104 | try { 105 | fs.writeFileSync(this.resourcePath, prettierSource); 106 | } catch (error) { 107 | return callback(error); 108 | } 109 | } 110 | 111 | callback(null, prettierSource, map); 112 | }; 113 | 114 | // for tests 115 | 116 | module.exports.__clearIgnoreManager = () => { 117 | ignoreManager = undefined; 118 | }; 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # prettier-loader 2 | 3 | [Prettier](https://github.com/prettier/prettier) loader for [Webpack](https://github.com/webpack/webpack). 4 | 5 | --- 6 | 7 | [![Travis][travis-badge]][travis] 8 | [![Coverage Status][coveralls-badge]][coveralls] 9 | [![Snyk Vulnerabilities for npm package][snyk-badge]][snyk] 10 | [![version][version-badge]][package] 11 | [![downloads][downloads-badge]][npmcharts] 12 | [![Node version support][node-version]][package] 13 | [![MIT License][license-badge]][license] 14 | [![PRs Welcome][prs-badge]][prs] 15 | [![Code of Conduct][coc-badge]][coc] 16 | 17 | ## Purpose 18 | 19 | Prettier is one of the best tools that really help developers not to waste time on codestyle. 20 | 21 | Listed below are some of the ways one could employ prettier in a project: 22 | 23 | - [Editor Integration](https://prettier.io/docs/en/editors.html) 24 | 25 | - Pros: 26 | - no overhead bootstrapping code 27 | - autoformatting on every save 28 | 29 | - Cons: 30 | - every developer needs to setup and configure editor's plugin manually 31 | - teammates can have conflicting plugin settings that might override default settings in an unpredictable way. Or one could forget to install `node_modules` and a globally installed `prettier` will be used instead, which might be of older version, etc. This leads to frustrating hiccups in the workflow and, potentially, bugs. 32 | 33 | - [Pre-commit Hook](https://prettier.io/docs/en/precommit.html) 34 | 35 | - Pros: 36 | - works in the background (i.e. developer doesn't have to think about it) 37 | - consistent prettier settings in the project 38 | 39 | - Cons: 40 | - you can not see prettier changes on save 41 | 42 | - [Watching For Changes](https://prettier.io/docs/en/watching-files.html) 43 | 44 | + Pros: 45 | - no overhead bootstrapping code 46 | - autoformatting on every save 47 | - works in the background 48 | - consistent prettier settings in the project 49 | 50 | - Cons: 51 | - if you already have another watcher (e.g. `webpack-dev-server` or `watchman`), you'll be wasting resources and your bundler will be triggered twice on every change: first by user, then by prettier formatting 52 | 53 | - [CLI](https://prettier.io/docs/en/cli.html) 54 | 55 | + Pros: 56 | - no overhead bootstrapping code 57 | 58 | - Cons: 59 | - you can not see prettier changes on save 60 | - prone to errors unless stored somewhere (e.g. npm-scripts) 61 | 62 | - [This Webpack Loader](https://www.npmjs.com/package/prettier-loader) 63 | 64 | + Pros: 65 | - autoformatting on every save (if working with webpack-dev-server) 66 | - works in the background 67 | - consistent prettier settings in the project 68 | - updates all the codebase when new prettier version is released 69 | 70 | - Cons: 71 | - works only on webpack-dependent projects 72 | 73 | In short, idea is to make source code auto-`prettier`-fy on every save. But to do it in a cross-IDE manner. Use of `webpack`, eliminates the need to install and configure plugins on each developer's machine and also provides better efficency, as no other watchers are needed. 74 | 75 | ## Features 76 | 77 | - support [prettier configuration files](https://prettier.io/docs/en/configuration.html): `.prettierrc`, `prettier.config.js`, `"prettier"` key in your `package.json` file 78 | - support of [ignoring code](https://prettier.io/docs/en/ignore.html) using both comments and `.prettierignore` file 79 | - override configuration files in loader options 80 | - zero configuration of loader options for supporting all features out of the box 81 | 82 | ## Requirements 83 | 84 | - webpack >= 2 85 | - prettier >= 1.6 86 | 87 | Create an issue on pr, if you __really__ need to support Webpack 1. 88 | 89 | ## Installation 90 | 91 | ``` 92 | npm install prettier-loader prettier --save-dev 93 | ``` 94 | 95 | ## Usage 96 | 97 | ### Minimal config example 98 | 99 | ```js 100 | // webpack.config.js 101 | module.exports = { 102 | // ... 103 | module: { 104 | rules: [ 105 | { 106 | test: /\.jsx?$/, 107 | use: { 108 | loader: 'prettier-loader', 109 | exclude: /node_modules/, 110 | } 111 | } 112 | ] 113 | } 114 | }; 115 | ``` 116 | 117 | ### Full config example with description 118 | 119 | ```js 120 | // webpack.config.js 121 | module.exports = { 122 | // ... 123 | module: { 124 | rules: [ 125 | { 126 | test: /\.jsx?$/, 127 | use: { 128 | loader: 'prettier-loader', 129 | 130 | // force this loader to run first if it's not first in loaders list 131 | enforce: 'pre', 132 | 133 | // avoid running prettier on all the files! 134 | // use it only on your source code and not on dependencies! 135 | exclude: /node_modules/, 136 | 137 | // additional prettier options assigned to options in 138 | // - .prettierrc, 139 | // - prettier.config.js, 140 | // - "prettier" property in package.json 141 | options: { 142 | trailingComma: 'es5', 143 | tabWidth: 4, 144 | 145 | // additional options object for resolveConfig method 146 | // @see https://prettier.io/docs/en/api.html#prettierresolveconfigfilepath-options 147 | resolveConfigOptions: { 148 | editorconfig: true, 149 | config: 'config/prettier.config.js' 150 | }, 151 | 152 | // skip rewriting source file. 153 | // if true, only prettier the output 154 | skipRewritingSource: false, 155 | 156 | // skip prettifying file at startup. 157 | // if true, prettier will be triggered after the modification of a file 158 | ignoreInitial: false, 159 | }, 160 | } 161 | } 162 | ] 163 | } 164 | }; 165 | ``` 166 | 167 | ### Working with HTML preprocessor 168 | 169 | If you work with HTML preprocessor (Twig, EJS, Nunjucks, ...), you may want to process the output stream. 170 | Still you don't want the input template to be rewritten with the output. 171 | In that case, you'll need to tell the loader to keep the source file unchanged. 172 | 173 | ```js 174 | // webpack.config.js 175 | module.exports = { 176 | // ... 177 | module: { 178 | rules: [ 179 | { 180 | test: /\.njk?$/, 181 | use: [ 182 | { 183 | loader: 'html-loader', 184 | }, 185 | { 186 | loader: 'prettier-loader', 187 | options: { 188 | skipRewritingSource: true, 189 | }, 190 | }, 191 | { 192 | loader: 'nunjucks-html-loader', 193 | }, 194 | ], 195 | } 196 | ] 197 | } 198 | }; 199 | ``` 200 | 201 | ## Pro Tip 202 | 203 | Install and use it only in development environment if you minimize code for production, don't do unnecessary work! 204 | 205 | ## Known issue 206 | 207 | As a loader, that is modifying source code, it has [known issue](https://github.com/iamolegga/prettier-loader/issues/1) with double compilation in watch mode. With current `webpack` API this problem can not be solved (the same problem exists in [other](https://github.com/rchaser53/awesome-prettier-loader/issues/3) [similar](https://github.com/webpack-contrib/eslint-loader/issues/303) [projects](https://github.com/hawkins/prettier-webpack-plugin/issues/2)). [Webpack maintainers are not going to help with this](https://github.com/webpack/webpack/issues/9763). 208 | 209 | ## Contributing 210 | 211 | All pull requests that respect next rules are welcome: 212 | 213 | - Before opening pull request to this repo run `npm t` to lint, prettify and run test on code. 214 | 215 | - All bugfixes and features should contain tests. 216 | 217 | ## License 218 | 219 | MIT License 220 | 221 | Copyright (c) 2017 Oleg Repin 222 | 223 | Permission is hereby granted, free of charge, to any person obtaining a copy 224 | of this software and associated documentation files (the "Software"), to deal 225 | in the Software without restriction, including without limitation the rights 226 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 227 | copies of the Software, and to permit persons to whom the Software is 228 | furnished to do so, subject to the following conditions: 229 | 230 | The above copyright notice and this permission notice shall be included in all 231 | copies or substantial portions of the Software. 232 | 233 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 234 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 235 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 236 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 237 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 238 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 239 | SOFTWARE. 240 | 241 | [travis-badge]: https://img.shields.io/travis/iamolegga/prettier-loader.svg?style=flat-square 242 | [travis]: https://travis-ci.org/iamolegga/prettier-loader 243 | [coveralls-badge]: https://img.shields.io/coveralls/github/iamolegga/prettier-loader.svg?style=flat-square 244 | [coveralls]: https://coveralls.io/github/iamolegga/prettier-loader?branch=master 245 | [snyk-badge]: https://img.shields.io/snyk/vulnerabilities/npm/prettier-loader 246 | [snyk]: https://snyk.io/test/github/iamolegga/prettier-loader 247 | [version-badge]: https://img.shields.io/npm/v/prettier-loader.svg?style=flat-square 248 | [package]: https://www.npmjs.com/package/prettier-loader 249 | [downloads-badge]: https://img.shields.io/npm/dm/prettier-loader.svg?style=flat-square 250 | [npmcharts]: https://npmcharts.com/compare/prettier-loader 251 | [node-version]: https://img.shields.io/node/v/prettier-loader.svg?style=flat-square 252 | [license-badge]: https://img.shields.io/npm/l/prettier-loader.svg?style=flat-square 253 | [license]: https://github.com/iamolegga/prettier-loader/blob/master/LICENSE 254 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square 255 | [prs]: http://makeapullrequest.com 256 | [coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square 257 | [coc]: https://github.com/iamolegga/prettier-loader/blob/master/CODE_OF_CONDUCT.md 258 | -------------------------------------------------------------------------------- /__tests__/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const prettier = require('prettier'); 4 | const rimraf = require('rimraf'); 5 | const webpack4 = require('webpack'); 6 | const webpack3 = require('webpack3'); 7 | const webpack2 = require('webpack2'); 8 | const __clearIgnoreManager = require('../prettier-loader').__clearIgnoreManager; 9 | 10 | /** 11 | * Loaders 12 | */ 13 | 14 | const loader = path.resolve(__dirname, '..', 'prettier-loader.js'); 15 | const checkOutputLoader = path.resolve( 16 | __dirname, 17 | 'utils', 18 | 'check-output-loader.js' 19 | ); 20 | 21 | /** 22 | * Helpers 23 | */ 24 | 25 | let testFolder; 26 | const SEPARATOR = path.sep; 27 | const testsParentFolder = path.join( 28 | __dirname, 29 | '..', 30 | '..', 31 | 'prettier-loader-tests' 32 | ); 33 | 34 | function prepare(engine, webpackConfiguration, files, entryFileName) { 35 | const testFiles = Object.keys(files).reduce((acc, fileName) => { 36 | const fullPath = path.join(testFolder, fileName); 37 | const content = files[fileName]; 38 | fs.writeFileSync(fullPath, content); 39 | acc[fullPath] = content; 40 | return acc; 41 | }, {}); 42 | 43 | return new Promise((resolve, reject) => { 44 | engine( 45 | Object.assign({}, webpackConfiguration, { 46 | entry: `.${SEPARATOR}${entryFileName}`, 47 | output: { path: testFolder }, 48 | }) 49 | ).run((error, stats) => { 50 | if (error) { 51 | return reject(error); 52 | } 53 | if (stats.hasErrors()) { 54 | return reject(stats.toJson().errors); 55 | } 56 | 57 | resolve(testFiles); 58 | }); 59 | }); 60 | } 61 | 62 | function getWebpackConfigWithRules(rules) { 63 | return { 64 | context: testFolder, 65 | module: { rules }, 66 | }; 67 | } 68 | 69 | function getContent(path) { 70 | return fs.readFileSync(path).toString('utf8'); 71 | } 72 | 73 | /** 74 | * Code examples 75 | */ 76 | 77 | const MATRIX_CODE = `matrix( 78 | 1, 0, 0, 79 | 0, 1, 0, 80 | 0, 0, 1 81 | )`; 82 | 83 | const CHAINING_CODE = `${'very().'.repeat(20)}long("chaining")`; 84 | 85 | /** 86 | * Tests settings 87 | */ 88 | 89 | beforeAll(() => { 90 | rimraf.sync(testsParentFolder); 91 | fs.mkdirSync(testsParentFolder); 92 | }); 93 | 94 | afterAll(() => { 95 | rimraf.sync(testsParentFolder); 96 | }); 97 | 98 | beforeEach(() => { 99 | testFolder = fs.mkdtempSync(`${testsParentFolder}${SEPARATOR}`); 100 | }); 101 | 102 | afterEach(() => { 103 | rimraf.sync(testFolder); 104 | // ignoreManager should be cleared after each test, for creating new instance 105 | // in next test, because we do every test in new temp directory 106 | // but ignoreManager already cached .prettierrc file of previous directory 107 | __clearIgnoreManager(); 108 | }); 109 | 110 | const engines = [webpack4, webpack3, webpack2]; 111 | let currentVersion = 4; 112 | 113 | /** 114 | * Tests 115 | */ 116 | 117 | for (const webpack of engines) { 118 | describe(`testing against webpack@${currentVersion--}`, () => { 119 | describe('pass options', () => { 120 | test('should work without loader-options and .prettierrc file', async () => { 121 | const entryFile = 'index.js'; 122 | const files = { [entryFile]: CHAINING_CODE }; 123 | 124 | const webpackConfiguration = getWebpackConfigWithRules([ 125 | { test: /\.js$/, use: { loader } }, 126 | ]); 127 | 128 | const testFiles = await prepare( 129 | webpack, 130 | webpackConfiguration, 131 | files, 132 | entryFile 133 | ); 134 | const entryPath = Object.keys(testFiles)[0]; 135 | const entryContent = getContent(entryPath); 136 | expect(prettier.check(entryContent)).toBe(true); 137 | }); 138 | 139 | test('should work with loader-options', async () => { 140 | const entryFile = 'index.js'; 141 | 142 | const prettierOptions = { tabWidth: 8 }; 143 | 144 | const files = { [entryFile]: CHAINING_CODE }; 145 | 146 | const webpackConfiguration = getWebpackConfigWithRules([ 147 | { test: /\.js$/, use: { loader, options: prettierOptions } }, 148 | ]); 149 | 150 | const testFiles = await prepare( 151 | webpack, 152 | webpackConfiguration, 153 | files, 154 | entryFile 155 | ); 156 | const entryPath = Object.keys(testFiles)[0]; 157 | const entryContent = getContent(entryPath); 158 | expect(prettier.check(entryContent, prettierOptions)).toBe(true); 159 | }); 160 | 161 | test('should work with .prettierrc file', async () => { 162 | const entryFile = 'index.js'; 163 | 164 | const prettierOptions = { tabWidth: 8 }; 165 | 166 | const files = { 167 | [entryFile]: CHAINING_CODE, 168 | '.prettierrc': JSON.stringify(prettierOptions), 169 | }; 170 | 171 | const webpackConfiguration = getWebpackConfigWithRules([ 172 | { test: /\.js$/, use: { loader } }, 173 | ]); 174 | 175 | const testFiles = await prepare( 176 | webpack, 177 | webpackConfiguration, 178 | files, 179 | entryFile 180 | ); 181 | const entryPath = Object.keys(testFiles).find(k => 182 | k.includes(entryFile) 183 | ); 184 | const entryContent = getContent(entryPath); 185 | expect(prettier.check(entryContent, prettierOptions)).toBe(true); 186 | }); 187 | 188 | test('should work with loader-options and .prettierrc file', async () => { 189 | const entryFile = 'index.js'; 190 | 191 | // create both, but loader rules should override prettierrc 192 | const prettierrcOptions = { tabWidth: 8, singleQuote: true }; 193 | const loaderOptions = { tabWidth: 4 }; 194 | 195 | const files = { 196 | [entryFile]: CHAINING_CODE, 197 | '.prettierrc': JSON.stringify(prettierrcOptions), 198 | }; 199 | 200 | const webpackConfiguration = getWebpackConfigWithRules([ 201 | { test: /\.js$/, use: { loader, options: loaderOptions } }, 202 | ]); 203 | 204 | const testFiles = await prepare( 205 | webpack, 206 | webpackConfiguration, 207 | files, 208 | entryFile 209 | ); 210 | const entryPath = Object.keys(testFiles).find(k => 211 | k.includes(entryFile) 212 | ); 213 | const entryContent = getContent(entryPath); 214 | expect( 215 | prettier.check( 216 | entryContent, 217 | Object.assign({}, prettierrcOptions, loaderOptions) 218 | ) 219 | ).toBe(true); 220 | }); 221 | 222 | test('should not rewrite entry file when skipRewritingSource is true', async () => { 223 | const entryFile = 'index.js'; 224 | const prettierOptions = { tabWidth: 8 }; 225 | const files = { [entryFile]: CHAINING_CODE }; 226 | const mockCheckResult = jest.fn(); 227 | 228 | const webpackConfiguration = getWebpackConfigWithRules([ 229 | { 230 | test: /\.js$/, 231 | use: [ 232 | { 233 | loader: checkOutputLoader, 234 | options: { checkResult: mockCheckResult }, 235 | }, 236 | { 237 | loader, 238 | options: { ...prettierOptions, skipRewritingSource: true }, 239 | }, 240 | ], 241 | }, 242 | ]); 243 | 244 | const testFiles = await prepare( 245 | webpack, 246 | webpackConfiguration, 247 | files, 248 | entryFile 249 | ); 250 | const entryPath = Object.keys(testFiles)[0]; 251 | const entryContent = getContent(entryPath); 252 | // entry file is not processed 253 | expect(prettier.check(entryContent, prettierOptions)).toBe(false); 254 | // entry file is left unchanged 255 | expect(entryContent === testFiles[entryPath]).toBe(true); 256 | // output stream is changed 257 | expect(mockCheckResult.mock.calls.length).toBe(1); 258 | expect(mockCheckResult.mock.calls[0][0]).not.toBe(entryContent); 259 | expect( 260 | prettier.check(mockCheckResult.mock.calls[0][0], prettierOptions) 261 | ).toBe(true); 262 | }); 263 | 264 | test('should not rewrite at initial loading', async () => { 265 | const entryFile = 'index.js'; 266 | const prettierOptions = { tabWidth: 8 }; 267 | const files = { [entryFile]: CHAINING_CODE }; 268 | const mockCheckResult = jest.fn(); 269 | 270 | const webpackConfiguration = getWebpackConfigWithRules([ 271 | { 272 | test: /\.js$/, 273 | use: [ 274 | { 275 | loader: checkOutputLoader, 276 | options: { checkResult: mockCheckResult }, 277 | }, 278 | { 279 | loader, 280 | options: { ...prettierOptions, ignoreInitial: true }, 281 | }, 282 | ], 283 | }, 284 | ]); 285 | 286 | const testFiles = await prepare( 287 | webpack, 288 | webpackConfiguration, 289 | files, 290 | entryFile 291 | ); 292 | const entryPath = Object.keys(testFiles)[0]; 293 | const entryContent = getContent(entryPath); 294 | // entry file is not processed 295 | expect(prettier.check(entryContent, prettierOptions)).toBe(false); 296 | // entry file is left unchanged 297 | expect(entryContent === testFiles[entryPath]).toBe(true); 298 | // output stream is left unchanged 299 | expect(mockCheckResult.mock.calls.length).toBe(1); 300 | expect(mockCheckResult.mock.calls[0][0]).toBe(entryContent); 301 | expect( 302 | prettier.check(mockCheckResult.mock.calls[0][0], prettierOptions) 303 | ).toBe(false); 304 | }); 305 | }); 306 | 307 | describe('ignoring', () => { 308 | test('should ignore using comments', async () => { 309 | const entryFile = 'index.js'; 310 | 311 | const files = { 312 | [entryFile]: ` 313 | ${CHAINING_CODE} 314 | // prettier-ignore 315 | ${MATRIX_CODE}`, 316 | }; 317 | 318 | const webpackConfiguration = getWebpackConfigWithRules([ 319 | { test: /\.js$/, use: { loader } }, 320 | ]); 321 | 322 | const testFiles = await prepare( 323 | webpack, 324 | webpackConfiguration, 325 | files, 326 | entryFile 327 | ); 328 | const entryPath = Object.keys(testFiles).find(k => 329 | k.includes(entryFile) 330 | ); 331 | const entryContent = getContent(entryPath); 332 | expect(entryContent).toMatch(MATRIX_CODE); 333 | }); 334 | 335 | test('should ignore using .prettierignore', async () => { 336 | const entryFile = 'index.js'; 337 | 338 | const files = { 339 | [entryFile]: MATRIX_CODE, 340 | '.prettierignore': entryFile, 341 | }; 342 | 343 | const webpackConfiguration = getWebpackConfigWithRules([ 344 | { test: /\.js$/, use: { loader } }, 345 | ]); 346 | 347 | const testFiles = await prepare( 348 | webpack, 349 | webpackConfiguration, 350 | files, 351 | entryFile 352 | ); 353 | const entryPath = Object.keys(testFiles).find(k => 354 | k.includes(entryFile) 355 | ); 356 | const entryContent = getContent(entryPath); 357 | expect(entryContent).toMatch(MATRIX_CODE); 358 | }); 359 | }); 360 | }); 361 | } 362 | --------------------------------------------------------------------------------