├── .gitignore ├── .npmrc ├── .travis.yml ├── README.md ├── bin ├── build.js └── compile.js ├── package.json ├── src ├── build.js ├── compile.js ├── compiled.js ├── extra-tests.js ├── find-es-support.js ├── get-config.js └── utils.js ├── test-dist ├── bin-main.bundle.js ├── bin-main.compiled.js ├── bin-main.features.json ├── main.bundle.js ├── main.compiled.js └── main.features.json └── test ├── bin-main.js ├── calc.js └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=http://registry.npmjs.org/ 2 | save-exact=true 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - node_modules 6 | notifications: 7 | email: false 8 | node_js: 9 | - '0.11' 10 | - '0.12' 11 | - '4.2.2' 12 | - '5' 13 | before_install: 14 | - npm i -g npm@^2.0.0 15 | before_script: 16 | - npm prune 17 | after_success: 18 | - npm run semantic-release 19 | branches: 20 | except: 21 | - "/^v\\d+\\.\\d+\\.\\d+$/" 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # compiled 2 | > Compiles the ES* bundle to your NodeJS version on install 3 | 4 | [![npm version](https://badge.fury.io/js/compiled.svg)](https://badge.fury.io/js/compiled) 5 | [![Build status][compiled-ci-image] ][compiled-ci-url] 6 | [![semantic-release][semantic-image] ][semantic-url] 7 | 8 | Read [JavaScript needs the compile step (on install)](http://glebbahmutov.com/blog/javascript-needs-compile-step/) blog post. If you want to precompile bundles instead of asking the client to do 9 | this, see [pre-compiled](https://github.com/bahmutov/pre-compiled). 10 | 11 | ## Install and use 12 | 13 | npm install -S compiled 14 | 15 | Add the configuration to your project's `package.json` file 16 | (I am assuming the root source file is `src/main.js`) 17 | 18 | ```json 19 | "config": { 20 | "compiled": { 21 | "dir": "dist", 22 | "files": ["src/main.js"] 23 | } 24 | } 25 | ``` 26 | 27 | You can list multiple files in `files` list - each bundle will be processed separately. 28 | 29 | Define the following scripts in the `package.json` 30 | 31 | ```json 32 | { 33 | "scripts": { 34 | "build": "build", 35 | "postinstall": "compile" 36 | }, 37 | "main": "dist/main.compiled.js" 38 | } 39 | ``` 40 | 41 | ## Multiple bundles 42 | 43 | Good example is the [left-behind](https://github.com/bahmutov/left-behind) repo. 44 | It has the main code and a bin script. Each is compiled separately. 45 | 46 | ## Force features 47 | 48 | You can force features to the list, maybe they are not detected by the feature tests. 49 | For example, to support the [new string repeat method](http://es6-features.org/#StringRepeating) 50 | 51 | ```json 52 | "config": { 53 | "compiled": { 54 | "dir": "dist", 55 | "files": ["src/main.js"], 56 | "features": ["StringMethods"] 57 | } 58 | } 59 | ``` 60 | 61 | Use the names returned by ES feature tests 62 | 63 | ## Debug and development 64 | 65 | If you run this code using `DEBUG=compiled` variable, it will print debug log messages. 66 | For example 67 | 68 | $ DEBUG=compiled npm run build 69 | > compiled@0.0.0-semantic-release test-build /Users/kensho/git/compiled 70 | > node bin/build.js src/main.js 71 | compiled building from +0ms src/main.js 72 | compiled saved bundle +69ms dist/bundle.js 73 | compiled scanning for es features +39ms dist/bundle.js 74 | compiled used ES features +15ms [ 'arrow', 'letConst', 'templateString' ] 75 | compiled saved file with found es features +3ms dist/es6-features.json 76 | 77 | ### Small print 78 | 79 | Author: Gleb Bahmutov © 2016 80 | 81 | * [@bahmutov](https://twitter.com/bahmutov) 82 | * [glebbahmutov.com](http://glebbahmutov.com) 83 | * [blog](http://glebbahmutov.com/blog/) 84 | 85 | License: MIT - do anything with the code, but don't blame me if it does not work. 86 | 87 | Spread the word: tweet, star on github, etc. 88 | 89 | Support: if you find any problems with this module, email / tweet / 90 | [open issue](https://github.com/bahmutov/compiled/issues) on Github 91 | 92 | ## MIT License 93 | 94 | Copyright (c) 2016 Gleb Bahmutov 95 | 96 | Permission is hereby granted, free of charge, to any person 97 | obtaining a copy of this software and associated documentation 98 | files (the "Software"), to deal in the Software without 99 | restriction, including without limitation the rights to use, 100 | copy, modify, merge, publish, distribute, sublicense, and/or sell 101 | copies of the Software, and to permit persons to whom the 102 | Software is furnished to do so, subject to the following 103 | conditions: 104 | 105 | The above copyright notice and this permission notice shall be 106 | included in all copies or substantial portions of the Software. 107 | 108 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 109 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 110 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 111 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 112 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 113 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 114 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 115 | OTHER DEALINGS IN THE SOFTWARE. 116 | 117 | [compiled-icon]: https://nodei.co/npm/compiled.png?downloads=true 118 | [compiled-url]: https://npmjs.org/package/compiled 119 | [compiled-ci-image]: https://travis-ci.org/bahmutov/compiled.png?branch=master 120 | [compiled-ci-url]: https://travis-ci.org/bahmutov/compiled 121 | [semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg 122 | [semantic-url]: https://github.com/semantic-release/semantic-release 123 | 124 | -------------------------------------------------------------------------------- /bin/build.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict' 4 | 5 | var help = [ 6 | 'USE: build', 7 | 'see https://github.com/bahmutov/compiled for help' 8 | ].join('\n') 9 | 10 | require('simple-bin-help')({ 11 | minArguments: 2, 12 | packagePath: __dirname + '/../package.json', 13 | help: help 14 | }) 15 | 16 | var build = require('..').build 17 | 18 | build() 19 | .catch(function (err) { 20 | console.error('problem building bundles') 21 | console.error(err.message) 22 | console.error(err.stack) 23 | process.exit(-1) 24 | }) 25 | -------------------------------------------------------------------------------- /bin/compile.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict' 4 | 5 | // do not run on itself when installing as 3rd party dependency 6 | var isForced = process.argv.some(function (arg) { 7 | return arg === '--force' || arg === '-f' 8 | }) 9 | var path = require('path') 10 | var fs = require('fs') 11 | if (!isForced) { 12 | var packageFilename = path.join(process.cwd(), 'package.json') 13 | if (fs.existsSync(packageFilename)) { 14 | var pkg = JSON.parse(fs.readFileSync(packageFilename)) 15 | if (pkg.name === 'compiled') { 16 | process.exit(0) 17 | } 18 | } 19 | } 20 | 21 | var help = [ 22 | 'USE: compile', 23 | ' - transpiles dist/bundle.js into dist/compiled.js' 24 | ].join('\n') 25 | 26 | require('simple-bin-help')({ 27 | minArguments: 2, 28 | packagePath: path.join(__dirname, '/../package.json'), 29 | help: help 30 | }) 31 | 32 | // require || takes care of self-build 33 | var compile = require('..').compile || 34 | require('../src/compile') 35 | 36 | compile() 37 | .catch(function (err) { 38 | console.error('problem compiling') 39 | console.error(err.message) 40 | console.error(err.stack) 41 | process.exit(-1) 42 | }) 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "compiled", 3 | "description": "Compiles the ES* bundle to your NodeJS version on install", 4 | "main": "src/compiled.js", 5 | "version": "0.0.0-semantic-release", 6 | "bin": { 7 | "build": "bin/build.js", 8 | "compile": "bin/compile.js" 9 | }, 10 | "preferGlobal": false, 11 | "engines": { 12 | "node": ">=0.11.0", 13 | "npm": ">=3.0.0" 14 | }, 15 | "files": [ 16 | "bin", 17 | "src" 18 | ], 19 | "scripts": { 20 | "test": "npm run lint && npm run test-build && npm run test-compile && node test-dist/main.compiled.js", 21 | "lint": "standard src/*.js test/*.js bin/build.js", 22 | "semantic-release": "semantic-release pre && npm publish && semantic-release post", 23 | "issues": "git-issues", 24 | "commit": "commit-wizard", 25 | "test-build": "DEBUG=compiled node bin/build.js", 26 | "test-compile": "DEBUG=compiled node bin/compile.js --force", 27 | "postinstall": "DEBUG=compiled node bin/compile.js", 28 | "size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/bahmutov/compiled.git" 33 | }, 34 | "keywords": [ 35 | "node", 36 | "nodejs", 37 | "version", 38 | "es5", 39 | "es6", 40 | "es7", 41 | "features", 42 | "babel", 43 | "rollup", 44 | "compile", 45 | "transpile" 46 | ], 47 | "author": "Gleb Bahmutov ", 48 | "license": "MIT", 49 | "bugs": { 50 | "url": "https://github.com/bahmutov/compiled/issues" 51 | }, 52 | "homepage": "https://github.com/bahmutov/compiled#readme", 53 | "devDependencies": { 54 | "condition-node-version": "1.2.0", 55 | "git-issues": "1.2.0", 56 | "pre-git": "3.3.0", 57 | "semantic-release": "^4.3.5", 58 | "standard": "5.4.1" 59 | }, 60 | "dependencies": { 61 | "babel-core": "6.4.0", 62 | "babel-plugin-transform-es2015-arrow-functions": "6.4.0", 63 | "babel-plugin-transform-es2015-block-scoping": "6.4.0", 64 | "babel-plugin-transform-es2015-destructuring": "6.4.0", 65 | "babel-plugin-transform-es2015-literals": "6.3.13", 66 | "babel-plugin-transform-es2015-parameters": "6.4.2", 67 | "babel-plugin-transform-es2015-shorthand-properties": "6.3.13", 68 | "babel-plugin-transform-es2015-spread": "6.4.0", 69 | "babel-plugin-transform-es2015-template-literals": "6.3.13", 70 | "babel-polyfill": "6.3.14", 71 | "check-more-types": "2.10.0", 72 | "debug": "2.2.0", 73 | "es-feature-tests": "0.3.0", 74 | "es-features-to-babel-plugins": "1.1.0", 75 | "lazy-ass": "1.3.0", 76 | "rollup": "0.24.1", 77 | "simple-bin-help": "1.6.0" 78 | }, 79 | "release": { 80 | "verifyConditions": { 81 | "path": "condition-node-version", 82 | "node": "4.2.2", 83 | "verbose": false 84 | } 85 | }, 86 | "config": { 87 | "compiled": { 88 | "dir": "test-dist", 89 | "files": [ 90 | "test/main.js", 91 | "test/bin-main.js" 92 | ], 93 | "features": [ 94 | "StringMethods" 95 | ] 96 | }, 97 | "pre-git": { 98 | "commit-msg": [ 99 | "simple" 100 | ], 101 | "pre-commit": [ 102 | "npm run lint", 103 | "npm run test-build", 104 | "npm run test-compile", 105 | "node test-dist/main.compiled.js" 106 | ], 107 | "pre-push": [ 108 | "npm run size" 109 | ], 110 | "post-commit": [], 111 | "post-merge": [] 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/build.js: -------------------------------------------------------------------------------- 1 | var debug = require('debug')('compiled') 2 | var la = require('lazy-ass') 3 | var is = require('check-more-types') 4 | var getConfig = require('./get-config') 5 | var utils = require('./utils') 6 | var path = require('path') 7 | var saveFile = require('fs').writeFileSync 8 | 9 | function roll (inputFilename, outputFilename) { 10 | var rollup = require('rollup') 11 | 12 | var firstLine = utils.getFirstLine(inputFilename) 13 | var hasHashBang = utils.isHashbang(firstLine) 14 | 15 | if (hasHashBang) { 16 | utils.removeFirstLine(inputFilename) 17 | } 18 | 19 | function restoreHashBang () { 20 | if (hasHashBang) { 21 | utils.restoreFirstLine(inputFilename, firstLine) 22 | utils.restoreFirstLine(outputFilename, firstLine + '\n') 23 | debug('restored hashbang in %s and added to %s', 24 | inputFilename, outputFilename) 25 | } 26 | } 27 | 28 | return rollup.rollup({ 29 | entry: inputFilename 30 | }).then(function (bundle) { 31 | return bundle.write({ 32 | format: 'cjs', 33 | dest: outputFilename 34 | }) 35 | .then(restoreHashBang, function (err) { 36 | restoreHashBang() 37 | throw err 38 | }) 39 | .then(function () { 40 | utils.finishWithEndline(outputFilename) 41 | }) 42 | .then(function () { 43 | debug('saved bundle', outputFilename) 44 | return outputFilename 45 | }) 46 | }) 47 | } 48 | 49 | function findUsedES6 (config, outputFilename, filename) { 50 | var testify = require('es-feature-tests/testify') 51 | debug('scanning for es features', filename) 52 | var output = testify.scan({ 53 | files: filename, 54 | output: 'json', 55 | enable: [] 56 | }) 57 | la(is.array(output), 'expected list of features', output) 58 | 59 | // var extraTests = require('./extra-tests') 60 | // if (extraTests.usesPromises(filename)) { 61 | // output.push('promises') 62 | // } 63 | 64 | if (is.array(config.features)) { 65 | debug('user required these features', config.features) 66 | config.features.filter(is.unemptyString).forEach(function (userListedFeature) { 67 | if (output.indexOf(userListedFeature) === -1) { 68 | output.push(userListedFeature) 69 | } 70 | }) 71 | } 72 | 73 | output = output.sort() 74 | debug('used ES features', output) 75 | 76 | saveFile(outputFilename, JSON.stringify(output, null, 2), 'utf-8') 77 | debug('saved file with found es features', outputFilename) 78 | } 79 | 80 | function buildBundle (config, inputFilename, toDir, name) { 81 | la(is.unemptyString(inputFilename), 'missing input filename', inputFilename) 82 | la(is.unemptyString(toDir), 'missing output dir name', toDir) 83 | la(is.unemptyString(name), 'missing bundle name', name) 84 | 85 | var outputFilename = path.join(toDir, utils.builtName(name)) 86 | debug('building %s from %s to %s', name, inputFilename, outputFilename) 87 | var featuresFilename = path.join(toDir, utils.featuresName(name)) 88 | 89 | return roll(inputFilename, outputFilename) 90 | .then(findUsedES6.bind(null, config, featuresFilename)) 91 | .catch(function (err) { 92 | console.error('problem building', inputFilename) 93 | console.error(err.message) 94 | console.error(err.stack) 95 | process.exit(-1) 96 | }) 97 | } 98 | 99 | function build (options) { 100 | var config = is.object(options) ? options : getConfig() 101 | debug('found %d files to build', config.files.length) 102 | var promises = config.files.map(function (filename) { 103 | return buildBundle(config, filename, config.dir, utils.bundleName(filename)) 104 | }) 105 | return Promise.all(promises) 106 | } 107 | 108 | module.exports = build 109 | -------------------------------------------------------------------------------- /src/compile.js: -------------------------------------------------------------------------------- 1 | var debug = require('debug')('compiled') 2 | var la = require('lazy-ass') 3 | var is = require('check-more-types') 4 | var utils = require('./utils') 5 | var findES6Support = require('./find-es-support') 6 | var getConfig = require('./get-config') 7 | var fs = require('fs') 8 | var babelMapping = require('es-features-to-babel-plugins') 9 | la(is.object(babelMapping), 'expected object with mapping', babelMapping) 10 | 11 | function transpile (supportedFeatures, neededFeatures, inputFilename, outputFilename, 12 | babelPolyfillModuleName) { 13 | var plugins = [] // plugin names 14 | // for now always include polyfill, 15 | // because hard to detect all edge cases when new methods are used 16 | var addBabelPolyfill = true 17 | 18 | function addUniquePlugin (name) { 19 | if (!name) { 20 | return 21 | } 22 | if (plugins.indexOf(name) === -1) { 23 | plugins.push(name) 24 | } 25 | } 26 | 27 | function addPlugin (names) { 28 | if (names === babelMapping.INCLUDE_BABEL_POLYFILL) { 29 | addBabelPolyfill = true 30 | return 31 | } 32 | 33 | if (typeof names === 'string') { 34 | return addUniquePlugin(names) 35 | } 36 | if (Array.isArray(names)) { 37 | names.forEach(addPlugin) 38 | } 39 | } 40 | 41 | neededFeatures.forEach(function (feature) { 42 | if (!supportedFeatures[feature]) { 43 | var needPlugins = babelMapping[feature] 44 | if (!needPlugins) { 45 | console.log('WARNING: feature', feature, 'is not handled') 46 | return 47 | } 48 | addPlugin(needPlugins) 49 | } 50 | }) 51 | 52 | debug('using plugins', plugins) 53 | 54 | var babel = require('babel-core') 55 | var options = { 56 | plugins: plugins 57 | } 58 | 59 | return new Promise(function (resolve, reject) { 60 | babel.transformFile(inputFilename, options, function (err, result) { 61 | debug('transformed file', inputFilename, 'was there an error?', err) 62 | if (err) { 63 | return reject(err) 64 | } 65 | 66 | var output = result.code 67 | if (addBabelPolyfill) { 68 | debug('adding Babel polyfill require') 69 | output = utils.addBabelRequire(output, babelPolyfillModuleName) 70 | } 71 | output = utils.finishTextWithEndline(output) 72 | 73 | require('fs').writeFileSync(outputFilename, output, 'utf-8') 74 | debug('saved file', outputFilename) 75 | resolve(outputFilename) 76 | }) 77 | }) 78 | } 79 | 80 | function compileBundle (es6results, inputFilename, esFeaturesFilename, outputFilename, 81 | moduleWithBabelPolyfill) { 82 | la(is.unemptyString(inputFilename), 'missing input filename') 83 | la(is.unemptyString(outputFilename), 'missing output filename') 84 | 85 | var name = utils.bundleName(inputFilename) 86 | 87 | var es6features = JSON.parse(fs.readFileSync(esFeaturesFilename, 'utf-8')) 88 | la(is.array(es6features), 89 | 'expected list of features from', esFeaturesFilename, 90 | 'got', es6features) 91 | debug('%s needs es6 features', name, es6features) 92 | 93 | return transpile(es6results, 94 | es6features, inputFilename, outputFilename, moduleWithBabelPolyfill) 95 | } 96 | 97 | var build = require('./build') 98 | 99 | function anyMissingBuiltFiles (config) { 100 | return config.files.some(function (filename) { 101 | var filenames = utils.formFilenames(config, filename) 102 | return !fs.existsSync(filenames.built) || 103 | !fs.existsSync(filenames.features) 104 | }) 105 | } 106 | 107 | function compileBuiltFiles (config, esFeatures) { 108 | la(is.object(esFeatures), 'missing supported ES6 features', 109 | 'config', config, 'es6 features', esFeatures) 110 | 111 | var promises = config.files.map(function (filename) { 112 | var filenames = utils.formFilenames(config, filename) 113 | 114 | debug('%s and %s => %s', 115 | filenames.built, filenames.features, filenames.compiled) 116 | 117 | return compileBundle(esFeatures, 118 | filenames.built, filenames.features, filenames.compiled, 119 | config.moduleWithBabelPolyfill) 120 | }) 121 | return Promise.all(promises) 122 | } 123 | 124 | function compile (options) { 125 | var config = is.object(options) ? options : getConfig() 126 | debug('found %d files to compile', config.files.length) 127 | 128 | var start = Promise.resolve(true) 129 | if (anyMissingBuiltFiles(config)) { 130 | console.log('missing build bundles, building ...') 131 | start = start.then(build) 132 | } else { 133 | debug('all built bundles are present') 134 | } 135 | 136 | var environmentESFeatures = is.object(config.esFeatures) 137 | ? Promise.resolve.bind(Promise, options.esFeatures) 138 | : findES6Support 139 | 140 | return start 141 | .then(environmentESFeatures) 142 | .then(compileBuiltFiles.bind(null, config)) 143 | } 144 | 145 | module.exports = compile 146 | -------------------------------------------------------------------------------- /src/compiled.js: -------------------------------------------------------------------------------- 1 | var build = require('./build') 2 | var compile = require('./compile') 3 | module.exports = { 4 | build: build, 5 | compile: compile, 6 | babelPolyfill: function () { 7 | require('babel-polyfill') 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/extra-tests.js: -------------------------------------------------------------------------------- 1 | var la = require('lazy-ass') 2 | var is = require('check-more-types') 3 | var read = require('fs').readFileSync 4 | 5 | function usesPromises (filename) { 6 | la(is.unemptyString(filename), 'expected filename') 7 | var source = read(filename, 'utf-8') 8 | return source.indexOf('Promise') !== -1 9 | } 10 | 11 | module.exports = { 12 | usesPromises: usesPromises 13 | } 14 | -------------------------------------------------------------------------------- /src/find-es-support.js: -------------------------------------------------------------------------------- 1 | /* 2 | To use: 3 | nvm 0.12 4 | node find-es-support.js > es.for.0.12.json 5 | */ 6 | var debug = require('debug')('compiled') 7 | var es6support = require('es-feature-tests') 8 | 9 | var findES6Support 10 | 11 | if (typeof Promise === 'undefined') { 12 | debug('missing Promises in the environment') 13 | findES6Support = function findES6Support () { 14 | return new Promise(function (resolve) { 15 | es6support('all', resolve) 16 | }) 17 | } 18 | } else { 19 | debug('using default es6 feature tests') 20 | findES6Support = function findES6Support () { 21 | return new Promise(function (resolve) { 22 | es6support('all', resolve) 23 | }) 24 | } 25 | } 26 | 27 | module.exports = findES6Support 28 | 29 | if (!module.parent) { 30 | es6support('all', function (features) { 31 | console.log(JSON.stringify(features, null, 2)) 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /src/get-config.js: -------------------------------------------------------------------------------- 1 | var debug = require('debug')('compiled') 2 | var la = require('lazy-ass') 3 | var is = require('check-more-types') 4 | var join = require('path').join 5 | var exists = require('fs').existsSync 6 | 7 | function noConfig (pkg) { 8 | return !pkg.config 9 | } 10 | 11 | function noCompiled (config) { 12 | return !config.compiled 13 | } 14 | 15 | var isValidConfig = is.object 16 | 17 | function getConfig () { 18 | var filename = join(process.cwd(), 'package.json') 19 | debug('loading compiled config from', filename) 20 | if (!exists(filename)) { 21 | throw new Error('Cannot find file ' + filename) 22 | } 23 | var pkg = require(filename) 24 | if (noConfig(pkg)) { 25 | debug(pkg) 26 | throw new Error('Cannot find config object in package ' + filename) 27 | } 28 | if (noCompiled(pkg.config)) { 29 | debug(pkg.config) 30 | throw new Error('Cannot find settings for compiled in config in package ' + filename) 31 | } 32 | la(isValidConfig(pkg.config.compiled), 'invalid compiled config in', filename) 33 | return pkg.config.compiled 34 | } 35 | 36 | function getBuildConfig () { 37 | var config = getConfig() 38 | var isConfig = is.schema({ 39 | dir: is.unemptyString, 40 | files: is.array 41 | }) 42 | la(isConfig(config), 'invalid compiled config', config) 43 | return config 44 | } 45 | 46 | module.exports = getBuildConfig 47 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | var la = require('lazy-ass') 2 | var is = require('check-more-types') 3 | var path = require('path') 4 | var join = path.join 5 | var fs = require('fs') 6 | 7 | var SELF_NAME = 'compiled' 8 | 9 | function bundleName (filename) { 10 | la(is.unemptyString(filename), 'expected filename', filename) 11 | return path.basename(filename, '.js') 12 | } 13 | 14 | function builtName (name) { 15 | la(is.unemptyString(name), 'expected name', name) 16 | return name + '.bundle.js' 17 | } 18 | 19 | function featuresName (name) { 20 | la(is.unemptyString(name), 'expected name', name) 21 | return name + '.features.json' 22 | } 23 | 24 | function compiledName (name) { 25 | la(is.unemptyString(name), 'expected name', name) 26 | return name + '.compiled.js' 27 | } 28 | 29 | function getFirstLine (filename) { 30 | var text = fs.readFileSync(filename, 'utf-8') 31 | return text.substr(0, text.indexOf('\n')) 32 | } 33 | 34 | function isHashbang (line) { 35 | return /^#!/.test(line) 36 | } 37 | 38 | function removeFirstLine (filename) { 39 | var text = fs.readFileSync(filename, 'utf-8') 40 | text = text.substr(text.indexOf('\n') + 1) 41 | fs.writeFileSync(filename, text, 'utf-8') 42 | } 43 | 44 | function restoreFirstLine (filename, line) { 45 | la(is.unemptyString(line), 'missing first line to add to', filename) 46 | var text = fs.readFileSync(filename, 'utf-8') 47 | text = line + '\n' + text 48 | fs.writeFileSync(filename, text, 'utf-8') 49 | } 50 | 51 | // TODO ends with newline could be its own small module 52 | function endsWithNewLines (text) { 53 | return /\n\n$/.test(text) 54 | } 55 | 56 | function finishTextWithEndline (text) { 57 | la(is.string(text), 'expected text', text) 58 | if (!endsWithNewLines(text)) { 59 | text += '\n' 60 | } 61 | return text 62 | } 63 | 64 | function finishWithEndline (filename) { 65 | var text = fs.readFileSync(filename, 'utf-8') 66 | text = finishTextWithEndline(text) 67 | fs.writeFileSync(filename, text, 'utf-8') 68 | } 69 | 70 | function formFilenames (config, filename) { 71 | la(is.object(config), 'missing config object', config) 72 | la(is.unemptyString(filename), 'expected filename', filename) 73 | 74 | var joinDir = join.bind(null, config.dir) 75 | 76 | var name = bundleName(filename) 77 | var builtFilename = joinDir(builtName(name)) 78 | var featuresFilename = joinDir(featuresName(name)) 79 | 80 | var compiledFilename = is.fn(config.formOutputFilename) 81 | ? config.formOutputFilename(name) 82 | : joinDir(compiledName(name)) 83 | 84 | return { 85 | built: builtFilename, 86 | features: featuresFilename, 87 | compiled: compiledFilename 88 | } 89 | } 90 | 91 | function addBabelRequire (text, moduleName) { 92 | text = text.trim() 93 | 94 | moduleName = moduleName || SELF_NAME 95 | var name = isSelfCompiling() ? '../src/compiled' : moduleName 96 | 97 | var requireLine = 'require(\'' + name + '\').babelPolyfill()\n' 98 | var firstNewLine = text.indexOf('\n') 99 | var firstLine = text.substr(0, firstNewLine + 1) 100 | 101 | var result 102 | 103 | if (/use strict/.test(firstLine)) { 104 | return firstLine + requireLine + text.substr(firstNewLine + 1) 105 | } 106 | if (isHashbang(firstLine)) { 107 | result = firstLine + '\n' + addBabelRequire(text.substr(firstNewLine + 1), moduleName) 108 | return result 109 | } 110 | return requireLine + text 111 | } 112 | 113 | function isSelfCompiling () { 114 | var packageFilename = join(process.cwd(), 'package.json') 115 | if (fs.existsSync(packageFilename)) { 116 | var pkg = JSON.parse(fs.readFileSync(packageFilename)) 117 | return pkg.name === SELF_NAME 118 | } 119 | } 120 | 121 | module.exports = { 122 | bundleName: bundleName, 123 | builtName: builtName, 124 | featuresName: featuresName, 125 | compiledName: compiledName, 126 | getFirstLine: getFirstLine, 127 | isHashbang: isHashbang, 128 | removeFirstLine: removeFirstLine, 129 | restoreFirstLine: restoreFirstLine, 130 | finishWithEndline: finishWithEndline, 131 | finishTextWithEndline: finishTextWithEndline, 132 | formFilenames: formFilenames, 133 | addBabelRequire: addBabelRequire, 134 | isSelfCompiling: isSelfCompiling 135 | } 136 | -------------------------------------------------------------------------------- /test-dist/bin-main.bundle.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | console.log(`example with hash bang line`) 6 | -------------------------------------------------------------------------------- /test-dist/bin-main.compiled.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | require('../src/compiled').babelPolyfill() 5 | 6 | console.log(`example with hash bang line`); 7 | -------------------------------------------------------------------------------- /test-dist/bin-main.features.json: -------------------------------------------------------------------------------- 1 | [ 2 | "StringMethods", 3 | "templateString" 4 | ] -------------------------------------------------------------------------------- /test-dist/main.bundle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // calc.js 4 | const add = (a, b) => a + b 5 | 6 | const a = 10 7 | const b = 2 8 | Promise.resolve(add(a, b)) 9 | .then(function (sum) { 10 | console.log(`${a} + ${b} = ${sum}`) 11 | }) 12 | 13 | const objectAdd = ({a, b}) => a + b 14 | console.log('Adding object properties', objectAdd({ a: 10, b: 2 })) 15 | 16 | console.log('binary literal', 0b101) 17 | 18 | // spread rest support 19 | var params = ['hello', true, 7] 20 | var other = [1, 2, ...params] // [ 1, 2, 'hello', true, 7 ] 21 | console.assert(other.length === 5, 'spread / rest not working') 22 | 23 | // object properties 24 | const foo = 'foo' 25 | const bar = 'bar' 26 | const o = {foo, bar} 27 | console.log(`object ${o.foo} and ${o.bar}`) 28 | 29 | // destructuring assignment 30 | var list = [1, 2, 3] 31 | var [first, , third] = list 32 | console.assert(first === 1, 'first element') 33 | console.assert(third === 3, 'third element') 34 | 35 | // TODO detect string repeat 36 | // string repeat 37 | const foo3 = 'foo'.repeat(3) 38 | console.assert(foo3 === 'foofoofoo', 'string repeat') 39 | -------------------------------------------------------------------------------- /test-dist/main.compiled.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | require('../src/compiled').babelPolyfill() 3 | 4 | // calc.js 5 | 6 | const add = (a, b) => a + b; 7 | 8 | const a = 10; 9 | const b = 2; 10 | Promise.resolve(add(a, b)).then(function (sum) { 11 | console.log(`${ a } + ${ b } = ${ sum }`); 12 | }); 13 | 14 | const objectAdd = _ref => { 15 | let a = _ref.a; 16 | let b = _ref.b; 17 | return a + b; 18 | }; 19 | console.log('Adding object properties', objectAdd({ a: 10, b: 2 })); 20 | 21 | console.log('binary literal', 0b101); 22 | 23 | // spread rest support 24 | var params = ['hello', true, 7]; 25 | var other = [1, 2].concat(params); // [ 1, 2, 'hello', true, 7 ] 26 | console.assert(other.length === 5, 'spread / rest not working'); 27 | 28 | // object properties 29 | const foo = 'foo'; 30 | const bar = 'bar'; 31 | const o = { foo, bar }; 32 | console.log(`object ${ o.foo } and ${ o.bar }`); 33 | 34 | // destructuring assignment 35 | var list = [1, 2, 3]; 36 | var first = list[0]; 37 | var third = list[2]; 38 | 39 | console.assert(first === 1, 'first element'); 40 | console.assert(third === 3, 'third element'); 41 | 42 | // TODO detect string repeat 43 | // string repeat 44 | const foo3 = 'foo'.repeat(3); 45 | console.assert(foo3 === 'foofoofoo', 'string repeat'); 46 | -------------------------------------------------------------------------------- /test-dist/main.features.json: -------------------------------------------------------------------------------- 1 | [ 2 | "StringMethods", 3 | "arrow", 4 | "conciseMethodProperty", 5 | "destructuring", 6 | "letConst", 7 | "numericLiteral", 8 | "parameterDestructuring", 9 | "spreadRest", 10 | "templateString" 11 | ] -------------------------------------------------------------------------------- /test/bin-main.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict' 4 | 5 | console.log(`example with hash bang line`) 6 | -------------------------------------------------------------------------------- /test/calc.js: -------------------------------------------------------------------------------- 1 | // calc.js 2 | export const add = (a, b) => a + b 3 | export const sub = (a, b) => a - b 4 | -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | // main.js 2 | import { add } from './calc' 3 | const a = 10 4 | const b = 2 5 | Promise.resolve(add(a, b)) 6 | .then(function (sum) { 7 | console.log(`${a} + ${b} = ${sum}`) 8 | }) 9 | 10 | const objectAdd = ({a, b}) => a + b 11 | console.log('Adding object properties', objectAdd({ a: 10, b: 2 })) 12 | 13 | console.log('binary literal', 0b101) 14 | 15 | // spread rest support 16 | var params = ['hello', true, 7] 17 | var other = [1, 2, ...params] // [ 1, 2, 'hello', true, 7 ] 18 | console.assert(other.length === 5, 'spread / rest not working') 19 | 20 | // object properties 21 | const foo = 'foo' 22 | const bar = 'bar' 23 | const o = {foo, bar} 24 | console.log(`object ${o.foo} and ${o.bar}`) 25 | 26 | // destructuring assignment 27 | var list = [1, 2, 3] 28 | var [first, , third] = list 29 | console.assert(first === 1, 'first element') 30 | console.assert(third === 3, 'third element') 31 | 32 | // TODO detect string repeat 33 | // string repeat 34 | const foo3 = 'foo'.repeat(3) 35 | console.assert(foo3 === 'foofoofoo', 'string repeat') 36 | --------------------------------------------------------------------------------