├── .babelrc ├── test ├── it │ ├── fixtures │ │ ├── .eslintrc │ │ └── bundle.js │ └── it.spec.js └── index.spec.js ├── .gitignore ├── .npmignore ├── .eslintrc ├── LICENSE ├── .travis.yml ├── package.json ├── README.md ├── gulpfile.js └── src └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /test/it/fixtures/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "sourceType": "module" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # NPM 2 | /node_modules/ 3 | /npm-debug.log 4 | /package-lock.json 5 | 6 | # Build 7 | /dist/ 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # NPM modules 2 | node_modules/ 3 | 4 | # Do not send dev files 5 | /test/ 6 | /src/ 7 | /.eslintrc 8 | /.babelrc 9 | /.nvmrc 10 | /.travis.yml 11 | /gulpfile.js 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "google", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | }, 6 | "env": { 7 | "node": true 8 | }, 9 | "rules": { 10 | "max-len": [2, 140, 2] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Mickael Jeanroy 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 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | ## 2 | # The MIT License (MIT) 3 | # 4 | # Copyright (c) 2017-2018 Mickael Jeanroy 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | ## 24 | 25 | language: node_js 26 | node_js: 27 | - "6" 28 | - "7" 29 | - "8" 30 | - "9" 31 | - "10" 32 | - "11" 33 | -------------------------------------------------------------------------------- /test/it/fixtures/bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Mickael Jeanroy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | /* eslint-disable */ 26 | 27 | export function sum(array) { return array.reduce((acc, x) => acc+x, 0) } 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollup-plugin-prettier", 3 | "version": "0.4.0", 4 | "description": "Run prettier formatter with rollup", 5 | "main": "dist/index.js", 6 | "author": "Mickael Jeanroy ", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "git@github.com:mjeanroy/rollup-plugin-prettier.git" 11 | }, 12 | "scripts": { 13 | "clean": "gulp clean", 14 | "lint": "gulp lint", 15 | "build": "gulp build", 16 | "test": "gulp test", 17 | "release": "gulp release:minor", 18 | "release:patch": "gulp release:patch", 19 | "release:minor": "gulp release:minor", 20 | "release:major": "gulp release:major" 21 | }, 22 | "keywords": [ 23 | "rollup", 24 | "rollup-plugin", 25 | "prettier" 26 | ], 27 | "dependencies": { 28 | "diff": "3.5.0", 29 | "magic-string": "0.25.1", 30 | "prettier": "^1.0.0" 31 | }, 32 | "devDependencies": { 33 | "@babel/core": "7.1.5", 34 | "@babel/parser": "7.1.5", 35 | "@babel/preset-env": "7.1.5", 36 | "del": "3.0.0", 37 | "eslint": "5.5.0", 38 | "eslint-config-google": "0.11.0", 39 | "fancy-log": "1.3.2", 40 | "gulp": "3.9.1", 41 | "gulp-babel": "8.0.0", 42 | "gulp-bump": "3.1.1", 43 | "gulp-eslint": "5.0.0", 44 | "gulp-git": "2.8.0", 45 | "gulp-jasmine": "4.0.0", 46 | "jasmine-core": "3.3.0", 47 | "q": "1.5.1", 48 | "rollup": "0.66.6", 49 | "run-sequence": "2.2.1", 50 | "tmp": "0.0.33" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rollup-plugin-prettier 2 | 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/mjeanroy/rollup-plugin-prettier.svg)](https://greenkeeper.io/) 4 | [![Build Status](https://travis-ci.org/mjeanroy/rollup-plugin-prettier.svg?branch=master)](https://travis-ci.org/mjeanroy/rollup-plugin-prettier) 5 | [![Npm version](https://badge.fury.io/js/rollup-plugin-prettier.svg)](https://badge.fury.io/js/rollup-plugin-prettier) 6 | 7 | Rollup plugin that can be used to run [prettier](http://npmjs.com/package/prettier) on the final bundle. 8 | 9 | ## How to use 10 | 11 | Install the plugin with NPM: 12 | 13 | `npm install --save-dev rollup-plugin-prettier` 14 | 15 | Then add it to your rollup configuration: 16 | 17 | const path = require('path'); 18 | const prettier = require('rollup-plugin-prettier'); 19 | 20 | ```javascript 21 | module.exports = { 22 | input: path.join(__dirname, 'src', 'index.js'), 23 | 24 | output: { 25 | file: path.join(__dirname, 'dist', 'bundle.js'), 26 | }, 27 | 28 | plugins: [ 29 | // Run plugin with prettier options. 30 | prettier({ 31 | tabWidth: 2, 32 | singleQuote: false, 33 | }), 34 | ], 35 | }; 36 | ``` 37 | 38 | ## Source Maps 39 | 40 | If source map is enabled in the global rollup options, then a source map will be generated on the formatted bundle (except if sourcemap are explicitely disabled in the prettier options). 41 | 42 | Note that this may take some time since `prettier` package is not able to generate a sourcemap and this plugin must compute the diff between the original bundle and the formatted result and generate the corresponding sourcemap: for this reason, sourcemap are disabled by default. 43 | 44 | Here is an example: 45 | 46 | ```javascript 47 | const path = require('path'); 48 | const prettier = require('rollup-plugin-prettier'); 49 | 50 | module.exports = { 51 | input: path.join(__dirname, 'src', 'index.js'), 52 | 53 | output: { 54 | file: path.join(__dirname, 'dist', 'bundle.js'), 55 | sourcemap: true, 56 | }, 57 | 58 | plugins: [ 59 | prettier({ 60 | sourceMap: true, // Can also be disabled/enabled here. 61 | }), 62 | ], 63 | }; 64 | ``` 65 | 66 | ## ChangeLogs 67 | 68 | - 0.4.0 69 | - Add compatibility with rollup >= 0.53 with output `sourcemap` option (see [rollup #1583](https://github.com/rollup/rollup/issues/1583)). 70 | - Avoid side-effect and do not change the plugin options (see [032be5](https://github.com/mjeanroy/rollup-plugin-prettier/commit/032be56317ab83cd87c2460f1dadc05a617c0d12)). 71 | - Various dependency updates. 72 | - 0.3.0 73 | - Support new `sourcemap` (lowercase) option of rollup. 74 | - Sourcemap can now be activated/disabled in the plugin options. 75 | - 0.2.0 76 | - Dependency update (`magic-string`) 77 | - 0.1.0 First release 78 | 79 | ## License 80 | 81 | MIT License (MIT) 82 | 83 | ## Contributing 84 | 85 | If you find a bug or think about enhancement, feel free to contribute and submit an issue or a pull request. 86 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Mickael Jeanroy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | const path = require('path'); 26 | const log = require('fancy-log'); 27 | const gulp = require('gulp'); 28 | const jasmine = require('gulp-jasmine'); 29 | const babel = require('gulp-babel'); 30 | const del = require('del'); 31 | const eslint = require('gulp-eslint'); 32 | const runSequence = require('run-sequence'); 33 | const git = require('gulp-git'); 34 | const bump = require('gulp-bump'); 35 | 36 | const ROOT = __dirname; 37 | const PKG_JSON = path.join(ROOT, 'package.json'); 38 | const SRC = path.join('src'); 39 | const TEST = path.join('test'); 40 | const DIST = path.join('dist'); 41 | 42 | gulp.task('test', ['build'], () => { 43 | const src = [ 44 | path.join(TEST, '**', '*.spec.js'), 45 | ]; 46 | 47 | return gulp.src(src).pipe(jasmine()); 48 | }); 49 | 50 | gulp.task('lint', () => { 51 | const src = [ 52 | path.join(SRC, '**', '*.js'), 53 | path.join(TEST, '**', '*.js'), 54 | path.join(ROOT, '*.js'), 55 | ]; 56 | 57 | return gulp.src(src) 58 | .pipe(eslint()) 59 | .pipe(eslint.format()) 60 | .pipe(eslint.failAfterError()); 61 | }); 62 | 63 | gulp.task('clean', () => { 64 | return del(DIST); 65 | }); 66 | 67 | gulp.task('build', ['lint', 'clean'], () => { 68 | return gulp.src(path.join(SRC, '*.js')) 69 | .pipe(babel()) 70 | .pipe(gulp.dest(DIST)); 71 | }); 72 | 73 | gulp.task('pretag', () => { 74 | return gulp.src([PKG_JSON, DIST]) 75 | .pipe(git.add({args: '-f'})) 76 | .pipe(git.commit('release: release version')); 77 | }); 78 | 79 | gulp.task('posttag', () => { 80 | return gulp.src(DIST) 81 | .pipe(git.rm({args: '-r'})) 82 | .pipe(git.commit('release: prepare next release')); 83 | }); 84 | 85 | gulp.task('tag', (done) => { 86 | const pkg = require(PKG_JSON); 87 | const version = pkg.version; 88 | git.tag(`v${version}`, `release: tag version ${version}`, done); 89 | }); 90 | 91 | ['major', 'minor', 'patch'].forEach((level) => { 92 | gulp.task(`bump:${level}`, () => { 93 | return gulp.src(PKG_JSON) 94 | .pipe(bump({type: level})) 95 | .on('error', (e) => log.error(e)) 96 | .pipe(gulp.dest(ROOT)); 97 | }); 98 | 99 | gulp.task('release:' + level, ['build'], () => { 100 | return runSequence(`bump:${level}`, 'pretag', 'tag', 'posttag'); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Mickael Jeanroy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | 'use strict'; 26 | 27 | const MagicString = require('magic-string'); 28 | const diff = require('diff'); 29 | const prettier = require('prettier'); 30 | 31 | const NAME = 'rollup-plugin-prettier'; 32 | 33 | module.exports = (options) => { 34 | let sourcemap = null; 35 | let newOptions = options; 36 | 37 | if (options && hasSourceMap(options)) { 38 | sourcemap = isSourceMapEnabled(options); 39 | 40 | // Delete custom option. 41 | newOptions = deleteSourceMap(options); 42 | 43 | // Do not send an empty option object. 44 | if (Object.keys(newOptions).length === 0) { 45 | newOptions = undefined; 46 | } 47 | } 48 | 49 | return { 50 | /** 51 | * Plugin name (used by rollup for error messages and warnings). 52 | * @type {string} 53 | */ 54 | name: NAME, 55 | 56 | /** 57 | * Function called by `rollup` that is used to read the `sourceMap` setting. 58 | * 59 | * @param {Object} opts Rollup options. 60 | * @return {void} 61 | */ 62 | options(opts = {}) { 63 | if (isNil(sourcemap)) { 64 | // Get the global `sourcemap` option on given object. 65 | // Should support: 66 | // - `sourcemap` (lowercase) option which is the name with rollup >= 0.48.0, 67 | // - `sourceMap` (camelcase) option which is the (deprecated) name with rollup < 0.48.0. 68 | const globalSourcemap = isSourceMapEnabled(opts); 69 | 70 | // Since rollup 0.48, sourcemap option can be set on the `output` object. 71 | const output = opts.output || {}; 72 | const outputSourceMap = Array.isArray(output) ? output.some(isSourceMapEnabled) : isSourceMapEnabled(output); 73 | 74 | // Enable or disable `sourcemap` generation. 75 | sourcemap = globalSourcemap || outputSourceMap; 76 | } 77 | }, 78 | 79 | /** 80 | * Function called by `rollup` before generating final bundle. 81 | * 82 | * @param {string} source Souce code of the final bundle. 83 | * @param {Object} outputOptions Output option. 84 | * @return {Object} The result containing a `code` property and, if a enabled, a `map` property. 85 | */ 86 | transformBundle(source, outputOptions) { 87 | const output = prettier.format(source, newOptions); 88 | const outputOptionsSourcemap = isNil(outputOptions) ? null : isSourceMapEnabled(outputOptions); 89 | 90 | // Should we generate sourcemap? 91 | // The sourcemap option may be a boolean or any truthy value (such as a `string`). 92 | // Note that this option should be false by default as it may take a (very) long time. 93 | if (!sourcemap && !outputOptionsSourcemap) { 94 | return {code: output}; 95 | } 96 | 97 | console.log(`[${NAME}] Sourcemap is enabled, computing diff is required`); 98 | console.log(`[${NAME}] This may take a moment (depends on the size of your bundle)`); 99 | 100 | const magicString = new MagicString(source); 101 | const changes = diff.diffChars(source, output); 102 | 103 | if (changes && changes.length > 0) { 104 | let idx = 0; 105 | 106 | changes.forEach((part) => { 107 | if (part.added) { 108 | magicString.prependLeft(idx, part.value); 109 | idx -= part.count; 110 | } else if (part.removed) { 111 | magicString.remove(idx, idx + part.count); 112 | } 113 | 114 | idx += part.count; 115 | }); 116 | } 117 | 118 | return { 119 | code: magicString.toString(), 120 | map: magicString.generateMap({ 121 | hires: true, 122 | }), 123 | }; 124 | }, 125 | }; 126 | }; 127 | 128 | 129 | const SOURCE_MAPS_OPTS = [ 130 | 'sourcemap', // Name of the property with rollup >= 0.48. 131 | 'sourceMap', // Name of the property with rollup < 0.48. 132 | ]; 133 | 134 | /** 135 | * Check if property exist on an object. 136 | * 137 | * @param {Object} o The object. 138 | * @param {string} prop The property name. 139 | * @return {boolean} `true` if property is defined on object, `false` otherwise. 140 | */ 141 | function has(o, prop) { 142 | return prop in o; 143 | } 144 | 145 | /** 146 | * Check if `sourcemap` option is defined on option object. 147 | * 148 | * @param {Object} opts Options. 149 | * @return {boolean} `true` if sourcemap is defined, `false` otherwise. 150 | */ 151 | function hasSourceMap(opts) { 152 | return SOURCE_MAPS_OPTS.some((p) => has(opts, p)); 153 | } 154 | 155 | /** 156 | * Check if `sourcemap` option is enable or not. 157 | * 158 | * @param {Object} opts Options. 159 | * @return {boolean} `true` if sourcemap is enabled, `false` otherwise. 160 | */ 161 | function isSourceMapEnabled(opts) { 162 | return !!(SOURCE_MAPS_OPTS.find((p) => opts[p])); 163 | } 164 | 165 | /** 166 | * Delete sourcemap option on object. 167 | * 168 | * @param {Object} opts The object. 169 | * @return {Object} Option object without `sourcemap` entry. 170 | */ 171 | function deleteSourceMap(opts) { 172 | const newOptions = {}; 173 | 174 | Object.keys(opts).forEach((k) => { 175 | if (SOURCE_MAPS_OPTS.indexOf(k) < 0) { 176 | newOptions[k] = opts[k]; 177 | } 178 | }); 179 | 180 | return newOptions; 181 | } 182 | 183 | /** 184 | * Check if value is `null` or `undefined`. 185 | * 186 | * @param {*} value Value to check. 187 | * @return {boolean} `true` if `value` is `null` or `undefined`, `false` otherwise. 188 | */ 189 | function isNil(value) { 190 | return value == null; 191 | } 192 | -------------------------------------------------------------------------------- /test/it/it.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Mickael Jeanroy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | 'use strict'; 26 | 27 | const fs = require('fs'); 28 | const path = require('path'); 29 | const rollup = require('rollup'); 30 | const tmp = require('tmp'); 31 | const Q = require('q'); 32 | const babelParser = require('@babel/parser'); 33 | const prettier = require('../../dist/index.js'); 34 | 35 | describe('rollup-plugin-prettier', () => { 36 | let tmpDir; 37 | 38 | beforeEach(() => { 39 | spyOn(console, 'log').and.callThrough(); 40 | }); 41 | 42 | beforeEach(() => { 43 | tmpDir = tmp.dirSync({ 44 | unsafeCleanup: true, 45 | }); 46 | }); 47 | 48 | afterEach(() => { 49 | tmpDir.removeCallback(); 50 | }); 51 | 52 | it('should run prettier on final bundle', (done) => { 53 | const output = path.join(tmpDir.name, 'bundle.js'); 54 | const config = { 55 | input: path.join(__dirname, 'fixtures', 'bundle.js'), 56 | output: { 57 | file: output, 58 | format: 'es', 59 | }, 60 | 61 | plugins: [ 62 | prettier({ 63 | parser: 'babylon', 64 | }), 65 | ], 66 | }; 67 | 68 | rollup.rollup(config) 69 | .then((bundle) => bundle.write(config.output)) 70 | .then(() => { 71 | fs.readFile(output, 'utf8', (err, data) => { 72 | if (err) { 73 | done.fail(err); 74 | } 75 | 76 | const content = data.toString(); 77 | 78 | expect(content).toBeDefined(); 79 | expect(content).toContain( 80 | 'function sum(array) {\n' + 81 | ' return array.reduce((acc, x) => acc + x, 0);\n' + 82 | '}' 83 | ); 84 | 85 | done(); 86 | }); 87 | }); 88 | }); 89 | 90 | it('should run prettier with @babel/parser instead of babylon', (done) => { 91 | const output = path.join(tmpDir.name, 'bundle.js'); 92 | const config = { 93 | input: path.join(__dirname, 'fixtures', 'bundle.js'), 94 | output: { 95 | file: output, 96 | format: 'es', 97 | }, 98 | 99 | plugins: [ 100 | prettier({ 101 | parser(text) { 102 | return babelParser.parse(text, { 103 | sourceType: 'module', 104 | }); 105 | }, 106 | }), 107 | ], 108 | }; 109 | 110 | rollup.rollup(config) 111 | .then((bundle) => bundle.write(config.output)) 112 | .then(() => { 113 | fs.readFile(output, 'utf8', (err, data) => { 114 | if (err) { 115 | done.fail(err); 116 | } 117 | 118 | const content = data.toString(); 119 | 120 | expect(content).toBeDefined(); 121 | expect(content).toContain( 122 | 'function sum(array) {\n' + 123 | ' return array.reduce((acc, x) => acc + x, 0);\n' + 124 | '}' 125 | ); 126 | 127 | done(); 128 | }); 129 | }); 130 | }); 131 | 132 | it('should run prettier on final bundle with sourcemap set in output option', (done) => { 133 | const output = path.join(tmpDir.name, 'bundle.js'); 134 | const config = { 135 | input: path.join(__dirname, 'fixtures', 'bundle.js'), 136 | 137 | output: { 138 | file: output, 139 | format: 'es', 140 | sourcemap: 'inline', 141 | }, 142 | 143 | plugins: [ 144 | prettier({ 145 | parser: 'babylon', 146 | }), 147 | ], 148 | }; 149 | 150 | console.log.and.stub(); 151 | 152 | rollup.rollup(config) 153 | .then((bundle) => bundle.write(config.output)) 154 | .then(() => { 155 | fs.readFile(output, 'utf8', (err, data) => { 156 | if (err) { 157 | done.fail(err); 158 | return; 159 | } 160 | 161 | const content = data.toString(); 162 | expect(content).toContain('//# sourceMappingURL'); 163 | expect(console.log).toHaveBeenCalledWith('[rollup-plugin-prettier] Sourcemap is enabled, computing diff is required'); 164 | done(); 165 | }); 166 | }) 167 | .catch((err) => { 168 | done.fail(err); 169 | }); 170 | }); 171 | 172 | it('should run prettier on final bundle with sourcemap set in output array option', (done) => { 173 | const output = path.join(tmpDir.name, 'bundle.js'); 174 | const config = { 175 | input: path.join(__dirname, 'fixtures', 'bundle.js'), 176 | 177 | output: [ 178 | {file: output, format: 'es', sourcemap: 'inline'}, 179 | ], 180 | 181 | plugins: [ 182 | prettier({ 183 | parser: 'babylon', 184 | }), 185 | ], 186 | }; 187 | 188 | console.log.and.stub(); 189 | 190 | rollup.rollup(config) 191 | .then((bundle) => ( 192 | Q.all(config.output.map((out) => bundle.write(out))) 193 | )) 194 | .then(() => { 195 | fs.readFile(output, 'utf8', (err, data) => { 196 | if (err) { 197 | done.fail(err); 198 | return; 199 | } 200 | 201 | const content = data.toString(); 202 | expect(content).toContain('//# sourceMappingURL'); 203 | expect(console.log).toHaveBeenCalledWith('[rollup-plugin-prettier] Sourcemap is enabled, computing diff is required'); 204 | done(); 205 | }); 206 | }) 207 | .catch((err) => { 208 | done.fail(err); 209 | }); 210 | }); 211 | 212 | it('should enable sourcemap (lowercase) on plugin', (done) => { 213 | const output = path.join(tmpDir.name, 'bundle.js'); 214 | const config = { 215 | input: path.join(__dirname, 'fixtures', 'bundle.js'), 216 | 217 | output: { 218 | file: output, 219 | format: 'es', 220 | }, 221 | 222 | plugins: [ 223 | prettier({ 224 | parser: 'babylon', 225 | sourcemap: true, 226 | }), 227 | ], 228 | }; 229 | 230 | console.log.and.stub(); 231 | 232 | rollup.rollup(config) 233 | .then((bundle) => bundle.write(config.output)) 234 | .then(() => { 235 | fs.readFile(output, 'utf8', (err, data) => { 236 | if (err) { 237 | done.fail(err); 238 | return; 239 | } 240 | 241 | expect(console.log).toHaveBeenCalledWith('[rollup-plugin-prettier] Sourcemap is enabled, computing diff is required'); 242 | done(); 243 | }); 244 | }) 245 | .catch((err) => { 246 | done.fail(err); 247 | }); 248 | }); 249 | 250 | it('should enable sourcemap (camelcase) on plugin', (done) => { 251 | const output = path.join(tmpDir.name, 'bundle.js'); 252 | const config = { 253 | input: path.join(__dirname, 'fixtures', 'bundle.js'), 254 | 255 | output: { 256 | file: output, 257 | format: 'es', 258 | }, 259 | 260 | plugins: [ 261 | prettier({ 262 | parser: 'babylon', 263 | sourceMap: true, 264 | }), 265 | ], 266 | }; 267 | 268 | console.log.and.stub(); 269 | 270 | rollup.rollup(config) 271 | .then((bundle) => bundle.write(config.output)) 272 | .then(() => { 273 | fs.readFile(output, 'utf8', (err, data) => { 274 | if (err) { 275 | done.fail(err); 276 | return; 277 | } 278 | 279 | expect(console.log).toHaveBeenCalledWith('[rollup-plugin-prettier] Sourcemap is enabled, computing diff is required'); 280 | done(); 281 | }); 282 | }) 283 | .catch((err) => { 284 | done.fail(err); 285 | }); 286 | }); 287 | }); 288 | -------------------------------------------------------------------------------- /test/index.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Mickael Jeanroy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | 'use strict'; 26 | 27 | const prettier = require('prettier'); 28 | const plugin = require('../dist/index.js'); 29 | 30 | describe('rollup-plugin-prettier', () => { 31 | beforeEach(() => { 32 | spyOn(console, 'log').and.callThrough(); 33 | }); 34 | 35 | it('should have a name', () => { 36 | const instance = plugin(); 37 | expect(instance.name).toBe('rollup-plugin-prettier'); 38 | }); 39 | 40 | it('should run prettier', () => { 41 | const instance = plugin({ 42 | parser: 'babylon', 43 | }); 44 | 45 | const code = 'var foo=0;var test="hello world";'; 46 | const result = instance.transformBundle(code); 47 | 48 | expect(console.log).not.toHaveBeenCalled(); 49 | expect(result.map).not.toBeDefined(); 50 | expect(result.code).toBe( 51 | 'var foo = 0;\n' + 52 | 'var test = "hello world";\n' 53 | ); 54 | }); 55 | 56 | it('should run prettier with sourceMap (camelcase with rollup < 0.48)', () => { 57 | const instance = plugin({ 58 | parser: 'babylon', 59 | }); 60 | 61 | instance.options({ 62 | sourceMap: true, 63 | }); 64 | 65 | console.log.and.stub(); 66 | 67 | const code = 'var foo=0;var test="hello world";'; 68 | const result = instance.transformBundle(code); 69 | 70 | expect(console.log).toHaveBeenCalledWith( 71 | '[rollup-plugin-prettier] Sourcemap is enabled, computing diff is required' 72 | ); 73 | 74 | expect(console.log).toHaveBeenCalledWith( 75 | '[rollup-plugin-prettier] This may take a moment (depends on the size of your bundle)' 76 | ); 77 | 78 | expect(result.map).toBeDefined(); 79 | expect(result.code).toBe( 80 | 'var foo = 0;\n' + 81 | 'var test = "hello world";\n' 82 | ); 83 | }); 84 | 85 | it('should run prettier with sourcemap (lowercase with rollup >= 0.48)', () => { 86 | const instance = plugin({ 87 | parser: 'babylon', 88 | }); 89 | 90 | instance.options({ 91 | sourcemap: true, 92 | }); 93 | 94 | console.log.and.stub(); 95 | 96 | const code = 'var foo=0;var test="hello world";'; 97 | const result = instance.transformBundle(code); 98 | 99 | expect(console.log).toHaveBeenCalledWith( 100 | '[rollup-plugin-prettier] Sourcemap is enabled, computing diff is required' 101 | ); 102 | 103 | expect(console.log).toHaveBeenCalledWith( 104 | '[rollup-plugin-prettier] This may take a moment (depends on the size of your bundle)' 105 | ); 106 | 107 | expect(result.map).toBeDefined(); 108 | expect(result.code).toBe( 109 | 'var foo = 0;\n' + 110 | 'var test = "hello world";\n' 111 | ); 112 | }); 113 | 114 | it('should run prettier with sourcemap in output options', () => { 115 | const instance = plugin({ 116 | parser: 'babylon', 117 | }); 118 | 119 | // The input options may not contain `sourcemap` entry with rollup >= 0.53. 120 | instance.options({}); 121 | 122 | console.log.and.stub(); 123 | 124 | const code = 'var foo=0;var test="hello world";'; 125 | const result = instance.transformBundle(code, { 126 | sourcemap: true, 127 | }); 128 | 129 | expect(console.log).toHaveBeenCalledWith( 130 | '[rollup-plugin-prettier] Sourcemap is enabled, computing diff is required' 131 | ); 132 | 133 | expect(console.log).toHaveBeenCalledWith( 134 | '[rollup-plugin-prettier] This may take a moment (depends on the size of your bundle)' 135 | ); 136 | 137 | expect(result.map).toBeDefined(); 138 | expect(result.code).toBe( 139 | 'var foo = 0;\n' + 140 | 'var test = "hello world";\n' 141 | ); 142 | }); 143 | 144 | it('should run prettier with sourcemap in output options (camelcase format)', () => { 145 | const instance = plugin({ 146 | parser: 'babylon', 147 | }); 148 | 149 | // The input options may not contain `sourcemap` entry with rollup >= 0.53. 150 | instance.options({}); 151 | 152 | console.log.and.stub(); 153 | 154 | const code = 'var foo=0;var test="hello world";'; 155 | const result = instance.transformBundle(code, { 156 | sourceMap: true, 157 | }); 158 | 159 | expect(console.log).toHaveBeenCalledWith( 160 | '[rollup-plugin-prettier] Sourcemap is enabled, computing diff is required' 161 | ); 162 | 163 | expect(console.log).toHaveBeenCalledWith( 164 | '[rollup-plugin-prettier] This may take a moment (depends on the size of your bundle)' 165 | ); 166 | 167 | expect(result.map).toBeDefined(); 168 | expect(result.code).toBe( 169 | 'var foo = 0;\n' + 170 | 'var test = "hello world";\n' 171 | ); 172 | }); 173 | 174 | it('should run prettier with sourcemap disabled in output options', () => { 175 | const instance = plugin({ 176 | parser: 'babylon', 177 | }); 178 | 179 | // The input options may not contain `sourcemap` entry with rollup >= 0.53. 180 | instance.options({}); 181 | 182 | console.log.and.stub(); 183 | 184 | const code = 'var foo=0;var test="hello world";'; 185 | const result = instance.transformBundle(code, { 186 | sourcemap: false, 187 | }); 188 | 189 | expect(console.log).not.toHaveBeenCalled(); 190 | expect(result.map).not.toBeDefined(); 191 | expect(result.code).toBe( 192 | 'var foo = 0;\n' + 193 | 'var test = "hello world";\n' 194 | ); 195 | }); 196 | 197 | it('should run prettier with options', () => { 198 | const options = { 199 | parser: 'babylon', 200 | singleQuote: true, 201 | }; 202 | 203 | const instance = plugin(options); 204 | 205 | instance.options({ 206 | sourceMap: false, 207 | }); 208 | 209 | const code = 'var foo=0;var test="hello world";'; 210 | const result = instance.transformBundle(code); 211 | 212 | expect(result.code).toBe( 213 | `var foo = 0;\n` + 214 | `var test = 'hello world';\n` 215 | ); 216 | }); 217 | 218 | it('should remove unnecessary spaces', () => { 219 | const instance = plugin({ 220 | parser: 'babylon', 221 | }); 222 | 223 | instance.options({ 224 | sourceMap: false, 225 | }); 226 | 227 | const code = 'var foo = 0;\nvar test = "hello world";'; 228 | const result = instance.transformBundle(code); 229 | 230 | expect(result.code).toBe( 231 | 'var foo = 0;\n' + 232 | 'var test = "hello world";\n' 233 | ); 234 | }); 235 | 236 | it('should add and remove characters', () => { 237 | const instance = plugin({ 238 | parser: 'babylon', 239 | }); 240 | 241 | instance.options({ 242 | sourceMap: false, 243 | }); 244 | 245 | const code = 'var foo = 0;var test = "hello world";'; 246 | const result = instance.transformBundle(code); 247 | 248 | expect(result.code).toBe( 249 | 'var foo = 0;\n' + 250 | 'var test = "hello world";\n' 251 | ); 252 | }); 253 | 254 | it('should avoid side effect and do not modify plugin options', () => { 255 | const options = { 256 | parser: 'babylon', 257 | sourceMap: false, 258 | }; 259 | 260 | const instance = plugin(options); 261 | instance.options({}); 262 | instance.transformBundle('var foo = 0;'); 263 | 264 | // It should not have been touched. 265 | expect(options).toEqual({ 266 | parser: 'babylon', 267 | sourceMap: false, 268 | }); 269 | }); 270 | 271 | it('should run prettier without sourcemap options', () => { 272 | const options = { 273 | parser: 'babylon', 274 | sourceMap: false, 275 | }; 276 | 277 | spyOn(prettier, 'format').and.callThrough(); 278 | 279 | const code = 'var foo = 0;'; 280 | const instance = plugin(options); 281 | instance.options({}); 282 | instance.transformBundle(code); 283 | 284 | expect(prettier.format).toHaveBeenCalledWith(code, { 285 | parser: 'babylon', 286 | }); 287 | 288 | expect(options).toEqual({ 289 | parser: 'babylon', 290 | sourceMap: false, 291 | }); 292 | }); 293 | 294 | it('should run prettier without sourcemap options and custom other options', () => { 295 | const options = { 296 | parser: 'babylon', 297 | sourceMap: false, 298 | singleQuote: true, 299 | }; 300 | 301 | spyOn(prettier, 'format').and.callThrough(); 302 | 303 | const code = 'var foo = 0;'; 304 | const instance = plugin(options); 305 | instance.options({}); 306 | instance.transformBundle(code); 307 | 308 | expect(prettier.format).toHaveBeenCalledWith(code, { 309 | parser: 'babylon', 310 | singleQuote: true, 311 | }); 312 | 313 | expect(options).toEqual({ 314 | parser: 'babylon', 315 | sourceMap: false, 316 | singleQuote: true, 317 | }); 318 | }); 319 | }); 320 | --------------------------------------------------------------------------------