├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json ├── src └── Bubleify.js └── tests ├── build.test.js ├── cli.test.js ├── error.test.js ├── files ├── error.export.js ├── error.template.js ├── quad.buble ├── quad.js └── simple.json ├── options.test.js ├── pkg ├── package.json └── quad.js └── sourcemap.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | indent_size = 2 10 | 11 | end_of_line = lf 12 | charset = utf-8 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb/base", 3 | "rules": { 4 | "no-underscore-dangle": 0, 5 | "import/no-mutable-exports": 0 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directory 30 | node_modules 31 | 32 | # Optional npm cache directory 33 | .npm 34 | 35 | # Optional REPL history 36 | .node_repl_history 37 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | tests/ 3 | .nyc_output/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | matrix: 3 | include: 4 | - node_js: "14" 5 | - node_js: "13" 6 | - node_js: "12" 7 | - node_js: "11" 8 | - node_js: "10" 9 | - node_js: "8" 10 | - node_js: "6" 11 | 12 | script: "[[ $BABEL == true ]] && npm run test:babel || npm test" 13 | after_success: 14 | - npm run travis-coverage 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jannick Garthen 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bubléify [![build status][1]][2] [![Coverage Status][3]][4] 2 | 3 | A browserify transform for [Bublé](https://www.npmjs.com/package/buble) to transform ES2015 to ES5. 4 | 5 | ## Installation 6 | 7 | ``` bash 8 | npm install --save-dev bubleify 9 | ``` 10 | 11 | ## Usage 12 | 13 | ### Node 14 | 15 | ``` javascript 16 | const browserify = require('browserify'); 17 | const bubleify = require('bubleify'); 18 | 19 | const b = browserify(); 20 | b.add('./file.es2015.js')); 21 | b.transform(bubleify, { 22 | target: { 23 | chrome: 48, 24 | firefox: 44, 25 | }, 26 | transforms: { 27 | arrow: true, 28 | defaultParameter: false, 29 | dangerousForOf: true, 30 | }, 31 | }); 32 | b.bundle(); 33 | ``` 34 | 35 | ### CLI 36 | 37 | ``` bash 38 | browserify script.js -o bundle.js -t [ bubleify ] 39 | ``` 40 | 41 | ## Options 42 | 43 | ### target: Object 44 | 45 | Target specifies a list of environments the output file should be compatible to. Bublè will decide based on this list which transforms should be used. 46 | 47 | ### transforms: Object 48 | 49 | Transforms define which ES2015 features should or should not be transformed into ES5. 50 | 51 | Bublèify by default disables the `module` transform to not throw an error when ES2015 `import` and `export` statements are used. If you want to use ES2015 modules you should add another transform to do so. 52 | 53 | Find a list of all transforms on the Bublè documentation in section [list of transforms](http://buble.surge.sh/guide/#list-of-transforms). For more detailed information about each transform also see [supported features](http://buble.surge.sh/guide/#supported-features) and [dangerous transforms](http://buble.surge.sh/guide/#dangerous-transforms). 54 | 55 | ### sourceMap: Boolean 56 | 57 | Define whether an inline source map should or should not be created by Bublé. 58 | 59 | Default is `true`. 60 | 61 | _Please note that browserify will not output any source map if debug mode is `false`, even if sourceMap was set to `true`._ 62 | 63 | ### extensions: Array 64 | 65 | The allowed file extensions that should be transformed with Bublé. Files included into the stream that do not match an extension will be ignored by Bubléify. 66 | 67 | Default is `['.js', '.jsx', '.es', '.es6']`. 68 | 69 | ### bubleError: Boolean 70 | 71 | Define whether the error generated by Bublè or a the Bublè error message as a string should be emitted in case of an error. 72 | 73 | Default is `false`. 74 | 75 | _Bublè may in some situations throw a custom error. Browserify will, when used on the command line, just output the call stack in this situation but does not show the error message. Turning this option on will display the error message but suppresses the call stack._ 76 | 77 | ## Credits 78 | 79 | Thanks goes to [Rich Harris](https://twitter.com/rich_harris) for the [Bublè](https://www.npmjs.com/package/buble) package. 80 | 81 | ## License 82 | 83 | Licensed under the [MIT License](https://opensource.org/licenses/mit-license.php). 84 | 85 | [1]: https://travis-ci.org/garthenweb/bubleify.svg 86 | [2]: https://travis-ci.org/garthenweb/bubleify 87 | [3]: https://coveralls.io/repos/github/garthenweb/bubleify/badge.svg?branch=master 88 | [4]: https://coveralls.io/github/garthenweb/bubleify?branch=master 89 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/Bubleify').default; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bubleify", 3 | "version": "2.0.0", 4 | "description": "Browserify transform that compiles es2015 to es5 using Bublé", 5 | "main": "index.js", 6 | "scripts": { 7 | "travis-coverage": "nyc report --reporter=text-lcov | coveralls", 8 | "coverage": "tap --cov --coverage-report=lcov tests/*.test.js", 9 | "tap:one": "tap --nyc-arg=--require --nyc-arg=babel-polyfill --timeout=60 --cov --coverage-report=text-summary", 10 | "tap:all": "npm run tap:one tests/*.test.js", 11 | "pretest": "npm run compile", 12 | "test": "npm run tap:all", 13 | "test:babel": "babel tests/*.test.js -d . && npm test", 14 | "lint": "eslint src/**.js tests/**.js", 15 | "compile": "babel -s -d lib/ src/", 16 | "publish": "npm run compile && npm run tap:all", 17 | "preversion": "npm run compile && npm run tap:all", 18 | "postversion": "git push && git push --tags" 19 | }, 20 | "keywords": [ 21 | "buble", 22 | "browserify", 23 | "browserify-transform", 24 | "es2015", 25 | "es5" 26 | ], 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/garthenweb/bubleify" 30 | }, 31 | "author": "Jannick Garthen ", 32 | "license": "MIT", 33 | "dependencies": { 34 | "buble": "^0.20.0" 35 | }, 36 | "devDependencies": { 37 | "babel-cli": "^6.26.0", 38 | "babel-polyfill": "^6.26.0", 39 | "babel-preset-es2015": "^6.24.1", 40 | "browserify": "^16.5.1", 41 | "convert-source-map": "^1.7.0", 42 | "coveralls": "^3.1.0", 43 | "eslint": "^7.2.0", 44 | "eslint-config-airbnb": "^18.1.0", 45 | "eslint-plugin-import": "^2.21.2", 46 | "lodash": "^4.17.15", 47 | "minimist": "^1.2.5", 48 | "mkdirp": "^1.0.4", 49 | "nyc": "^15.1.0", 50 | "tap": "^12.7.0" 51 | }, 52 | "bugs": { 53 | "url": "https://github.com/garthenweb/bubleify/issues" 54 | }, 55 | "engines": { 56 | "node": ">=6.0.0" 57 | }, 58 | "nyc": { 59 | "exclude": [ 60 | "tests/**" 61 | ] 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Bubleify.js: -------------------------------------------------------------------------------- 1 | import { transform } from 'buble'; 2 | import { Transform, PassThrough } from 'stream'; 3 | import { EOL } from 'os'; 4 | import { extname } from 'path'; 5 | 6 | class Bubleify extends Transform { 7 | constructor(filename, options) { 8 | super(); 9 | this._data = ''; 10 | this._filename = filename; 11 | this._options = options; 12 | } 13 | 14 | get _bubleOptions() { 15 | const defaults = { source: this._filename }; 16 | const options = Object.assign(defaults, this._options); 17 | 18 | // copy properties to not modify the existing objects 19 | // set default transforms with deactivated modules 20 | options.transforms = Object.assign({ modules: false }, this._options.transforms); 21 | options.target = Object.assign({}, this._options.target); 22 | 23 | // remove browserify options 24 | delete options._flags; 25 | delete options.sourceMap; 26 | delete options.extensions; 27 | delete options.bubleError; 28 | delete options.target._; 29 | 30 | return options; 31 | } 32 | 33 | _transform(buf, enc, cb) { 34 | this._data += buf; 35 | cb(); 36 | } 37 | 38 | _flush(cb) { 39 | try { 40 | const result = transform(this._data, this._bubleOptions); 41 | let { code } = result; 42 | 43 | if (this._options.sourceMap) { 44 | // append sourcemaps to code 45 | code += `${EOL}//# sourceMappingURL=${result.map.toUrl()}`; 46 | } 47 | 48 | this.emit('bubleify', result, this._filename); 49 | this.push(code); 50 | } catch (err) { 51 | // emit buble error message instead of the default error 52 | if (this._options.bubleError && err.snippet) { 53 | this.emit('error', `---${EOL}${err.snippet}${EOL}${EOL}${err.message}${EOL}`); 54 | } else { 55 | this.emit('error', err); 56 | } 57 | return; 58 | } 59 | cb(); 60 | } 61 | } 62 | 63 | export default (filename, options) => { 64 | // get extensions or defaults 65 | let { extensions = ['.js', '.jsx', '.es', '.es6'] } = options; 66 | // convert to json 67 | extensions = Array.isArray(extensions) ? extensions : [extensions]; 68 | 69 | const enrishedOptions = Object.assign({ 70 | sourceMap: true, 71 | bubleError: false, 72 | }, options, { extensions }); 73 | 74 | const shouldIgnoreFile = extensions.indexOf(extname(filename)) === -1; 75 | // return empty stream for files that should not be transformed 76 | if (shouldIgnoreFile) { 77 | // eslint-disable-next-line new-cap 78 | return PassThrough(); 79 | } 80 | 81 | return new Bubleify(filename, enrishedOptions); 82 | }; 83 | -------------------------------------------------------------------------------- /tests/build.test.js: -------------------------------------------------------------------------------- 1 | const { test } = require('tap'); 2 | const vm = require('vm'); 3 | 4 | const browserify = require('browserify'); 5 | const bubleify = require('../index'); 6 | 7 | const quadPath = require.resolve('./files/quad.js'); 8 | const quadPkgPath = require.resolve('./pkg/quad.js'); 9 | const simpleJSONPath = require.resolve('./files/simple.json'); 10 | 11 | const runContextQuad5 = (src) => { 12 | const sandbox = {}; 13 | const srcStr = typeof src !== 'string' ? src.toString('utf8') : src; 14 | vm.runInNewContext(srcStr, sandbox); 15 | return sandbox.require('quad')(5); 16 | }; 17 | 18 | test('simple js api', (t) => { 19 | const b = browserify(); 20 | b.require(quadPath, { expose: 'quad' }); 21 | b.transform(bubleify); 22 | b.bundle((err, src) => { 23 | t.error(err); 24 | t.equal(runContextQuad5(src), 25); 25 | t.end(); 26 | }); 27 | }); 28 | 29 | test('simple js api with package.json', (t) => { 30 | const b = browserify(); 31 | b.require(quadPkgPath, { expose: 'quad' }); 32 | b.bundle((err, src) => { 33 | t.error(err); 34 | t.equal(runContextQuad5(src), 25); 35 | t.end(); 36 | }); 37 | }); 38 | 39 | test('ignoring of undefined extensions (json)', (t) => { 40 | const b = browserify(); 41 | b.require(simpleJSONPath); 42 | b.transform(bubleify); 43 | b.bundle((err, src) => { 44 | t.error(err); 45 | t.match(src.toString('utf8'), /module.exports={"test":"test","test2":5}/); 46 | t.end(); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /tests/cli.test.js: -------------------------------------------------------------------------------- 1 | const { test } = require('tap'); 2 | const vm = require('vm'); 3 | const { spawn } = require('child_process'); 4 | 5 | const quadPath = require.resolve('./files/quad.js'); 6 | const quadPathBuble = require.resolve('./files/quad.buble'); 7 | const bubleifyPath = require.resolve('../index.js'); 8 | const browserifyCmd = require.resolve('../node_modules/.bin/browserify'); 9 | 10 | const testContextQuad5 = (b, t) => { 11 | let out = ''; 12 | let err = ''; 13 | b.stdout.on('data', (buf) => { 14 | out += buf; 15 | }); 16 | b.stderr.on('data', (buf) => { 17 | err += buf; 18 | }); 19 | 20 | b.on('error', (processErr) => { 21 | throw processErr; 22 | }); 23 | 24 | b.on('exit', () => { 25 | const sandbox = {}; 26 | const srcStr = typeof out !== 'string' ? out.toString('utf8') : out; 27 | vm.runInNewContext(srcStr, sandbox); 28 | const result = sandbox.require('quad')(5); 29 | 30 | t.error(err); 31 | t.equal(result, 25); 32 | t.end(); 33 | }); 34 | }; 35 | 36 | test('simple cli', (t) => { 37 | const bProcess = spawn(browserifyCmd, [ 38 | '-r', `${quadPath}:quad`, 39 | '-t', '[', bubleifyPath, ']', 40 | ], { shell: true }); 41 | 42 | testContextQuad5(bProcess, t); 43 | }); 44 | 45 | test('cli with custom extension', (t) => { 46 | const bProcess = spawn(browserifyCmd, [ 47 | '-r', `${quadPathBuble}:quad`, 48 | '-t', '[', bubleifyPath, '--extensions', '.buble', ']', 49 | ], { shell: true }); 50 | 51 | testContextQuad5(bProcess, t); 52 | }); 53 | 54 | test('cli with target', (t) => { 55 | const bProcess = spawn(browserifyCmd, [ 56 | '-r', `${quadPath}:quad`, 57 | '-t', '[', bubleifyPath, '--target', '[', '--ie', '11', ']', ']', 58 | ], { shell: true }); 59 | 60 | testContextQuad5(bProcess, t); 61 | }); 62 | -------------------------------------------------------------------------------- /tests/error.test.js: -------------------------------------------------------------------------------- 1 | const { test } = require('tap'); 2 | 3 | const browserify = require('browserify'); 4 | const bubleify = require('../index'); 5 | 6 | const errorExportPath = require.resolve('./files/error.export.js'); 7 | const errorTemplatePath = require.resolve('./files/error.template.js'); 8 | 9 | test('throw on error', (t) => { 10 | const b = browserify({ debug: true }); 11 | b.require(errorExportPath); 12 | b.transform(bubleify, { transforms: { modules: true } }); 13 | b.bundle((err, src) => { 14 | t.notOk(src); 15 | t.end(); 16 | }); 17 | }); 18 | 19 | test('throw on error with buble custom message', (t) => { 20 | const b = browserify({ debug: true }); 21 | b.require(errorTemplatePath); 22 | b.transform(bubleify, { bubleError: true }); 23 | b.bundle((err, src) => { 24 | t.notOk(src); 25 | t.type(err, 'string'); 26 | t.match(err, /myTag`test`/ig, 'err should contain the failing code'); 27 | t.end(); 28 | }); 29 | }); 30 | 31 | -------------------------------------------------------------------------------- /tests/files/error.export.js: -------------------------------------------------------------------------------- 1 | export default () => null; 2 | -------------------------------------------------------------------------------- /tests/files/error.template.js: -------------------------------------------------------------------------------- 1 | myTag`test`; 2 | -------------------------------------------------------------------------------- /tests/files/quad.buble: -------------------------------------------------------------------------------- 1 | const quad = (x = 5) => x * x; 2 | 3 | module.exports = quad; 4 | -------------------------------------------------------------------------------- /tests/files/quad.js: -------------------------------------------------------------------------------- 1 | const quad = (x = 5) => x * x; 2 | 3 | module.exports = quad; 4 | -------------------------------------------------------------------------------- /tests/files/simple.json: -------------------------------------------------------------------------------- 1 | {"test":"test","test2":5} 2 | -------------------------------------------------------------------------------- /tests/options.test.js: -------------------------------------------------------------------------------- 1 | const { test } = require('tap'); 2 | const bubleify = require('../index'); 3 | 4 | test('buble passed options', (t) => { 5 | const filename = './test.js'; 6 | const _flags = {}; 7 | const transforms = { myCustomTransform: false }; 8 | const target = { Chrome: 50, Firefox: 43, Edge: 12 }; 9 | 10 | const tBuble = bubleify(filename, { _flags, transforms, target }); 11 | 12 | t.equal(tBuble._bubleOptions._flags, undefined); 13 | t.equal(tBuble._bubleOptions.extensions, undefined); 14 | t.equal(tBuble._bubleOptions.sourceMap, undefined); 15 | t.equal(tBuble._bubleOptions.bubleError, undefined); 16 | 17 | t.match(tBuble._bubleOptions.transforms, Object.assign({ modules: false }, transforms)); 18 | t.notEqual(tBuble._bubleOptions.transforms, transforms); 19 | 20 | t.match(tBuble._bubleOptions.target, target); 21 | t.notEqual(tBuble._bubleOptions.target, target); 22 | 23 | t.equal(tBuble._bubleOptions.source, filename); 24 | t.end(); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "browserify": { 3 | "transform": [ 4 | "../../" 5 | ] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/pkg/quad.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../files/quad'); 2 | -------------------------------------------------------------------------------- /tests/sourcemap.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { test } = require('tap'); 3 | 4 | const convert = require('convert-source-map'); 5 | const browserify = require('browserify'); 6 | const bubleify = require('../index'); 7 | 8 | const quadPath = require.resolve('./files/quad.js'); 9 | const quadCode = fs.readFileSync(quadPath, 'utf-8'); 10 | 11 | test('sourcemap in debug mode', (t) => { 12 | const b = browserify({ debug: true }); 13 | b.require(quadPath); 14 | b.transform(bubleify); 15 | b.bundle((err, src) => { 16 | t.error(err); 17 | const sourceMap = convert.fromSource(src.toString()).toObject(); 18 | t.equal(sourceMap.sourcesContent[1], quadCode); 19 | t.end(); 20 | }); 21 | }); 22 | 23 | test('disabling sourcemap via bubleify option', (t) => { 24 | const b = browserify({ debug: true }); 25 | b.require(quadPath); 26 | b.transform(bubleify, { sourceMap: false }); 27 | b.bundle((err, src) => { 28 | t.error(err); 29 | const sourceMap = convert.fromSource(src.toString()).toObject(); 30 | t.notEqual(sourceMap.sourcesContent[1], quadCode); 31 | t.end(); 32 | }); 33 | }); 34 | --------------------------------------------------------------------------------