├── test ├── embeded-files │ ├── hw.txt │ ├── index.js │ └── package.json ├── mocha.opts ├── js-yaml-148 │ ├── index.js │ └── package.json ├── gulp-test-170 │ ├── index.js │ ├── gulpfile.js │ └── package.json ├── express-test │ ├── views │ │ ├── index.jade │ │ ├── error.jade │ │ └── layout.jade │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── users.js │ │ └── index.js │ ├── package.json │ ├── app.js │ └── bin │ │ └── www ├── flags-test │ ├── index.js │ └── package.json ├── ignoreFlags-test │ ├── package.json │ └── index.js └── test.js ├── lib ├── index.js ├── log.js ├── monkeypatch.js ├── embed.js ├── sourcemap.js ├── bundle.js ├── transform.js ├── propmangle.js ├── utils.js ├── mozilla-ast.js ├── scope.js └── exe.js ├── circle.yml ├── .gitignore ├── tools ├── exports.js ├── props.html └── node.js ├── LICENSE.txt ├── LICENSE ├── package.json ├── README.md ├── CHANGELOG.md └── bin └── node-compiler /test/embeded-files/hw.txt: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --timeout 100000000 2 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./exe"); 2 | -------------------------------------------------------------------------------- /test/js-yaml-148/index.js: -------------------------------------------------------------------------------- 1 | require('js-yaml'); 2 | 3 | console.log(true); 4 | -------------------------------------------------------------------------------- /test/gulp-test-170/index.js: -------------------------------------------------------------------------------- 1 | // it probably executed. 2 | console.log(true); 3 | -------------------------------------------------------------------------------- /test/express-test/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 5.7.0 4 | 5 | test: 6 | override: 7 | - npm test: 8 | timeout: 5400 9 | -------------------------------------------------------------------------------- /test/express-test/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /test/express-test/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /test/express-test/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /test/express-test/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /test/express-test/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /test/embeded-files/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Embedded Files Test 3 | * 4 | * @author Jared Allard 5 | * @version 0.0.1 6 | * @license MIT 7 | **/ 8 | 9 | const nexeres = require('nexeres'); 10 | 11 | process.stdout.write(nexeres.get('hw.txt').toString('ascii')); 12 | -------------------------------------------------------------------------------- /test/flags-test/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test to verify if we support strict mode (flags) 3 | * 4 | * @author Jared Allard 5 | * @version 0.0.1 6 | * @license MIT 7 | **/ 8 | 9 | // "use strict"; w/o --use_strict 10 | 11 | let isStrict = (function() { return !this; })(); 12 | 13 | console.log(isStrict); 14 | -------------------------------------------------------------------------------- /test/flags-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flags-test", 3 | "nexe": { 4 | "input": "./index.js", 5 | "output": "test.nex", 6 | "temp": "../src", 7 | "runtime": { 8 | "framework": "node", 9 | "version": "5.7.0", 10 | "ignoreFlags": false, 11 | "js-flags": "--use_strict", 12 | "node-args": "" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/ignoreFlags-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flags-test", 3 | "nexe": { 4 | "input": "./index.js", 5 | "output": "test.nex", 6 | "temp": "../src", 7 | "runtime": { 8 | "framework": "node", 9 | "version": "5.7.0", 10 | "ignoreFlags": true, 11 | "node-args": "" 12 | } 13 | }, 14 | "dependencies": {} 15 | } 16 | -------------------------------------------------------------------------------- /test/ignoreFlags-test/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test to verify if we support strict mode (flags) 3 | * 4 | * @author Jared Allard 5 | * @version 0.0.1 6 | * @license MIT 7 | **/ 8 | 9 | // test.nex --help 10 | 11 | let status = false; 12 | 13 | // console.log(process.argv); 14 | 15 | if(process.argv[2]) { 16 | status = true; 17 | } 18 | 19 | console.log(status); 20 | -------------------------------------------------------------------------------- /test/gulp-test-170/gulpfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | let gulp = require('gulp'); 4 | let nexe = require('nexe'); 5 | 6 | gulp.task( "compile", ( callback ) => { 7 | let options = { 8 | input: "index.js", 9 | output: "test.nex", 10 | python: "python", 11 | nodeTempDir: "./tmp", 12 | nodeVersion: "latest", 13 | flags: false, 14 | framework: "nodejs" 15 | }; 16 | 17 | nexe.compile( options, ( err ) => { 18 | console.log( err ); 19 | callback( err ); 20 | } ); 21 | } ); 22 | 23 | gulp.task('default', ['compile']); 24 | -------------------------------------------------------------------------------- /test/js-yaml-148/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-yaml-148", 3 | "version": "1.0.0", 4 | "description": "#148 js-yaml fails to work", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Jared Allard ", 10 | "license": "MIT", 11 | "dependencies": { 12 | "js-yaml": "^3.5.2" 13 | }, 14 | "nexe": { 15 | "input": "index.js", 16 | "output": "test.nex", 17 | "temp": "../src", 18 | "runtime": { 19 | "version": "5.7.0", 20 | "framework": "node" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/embeded-files/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "embededfiles-test", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node index.js" 7 | }, 8 | "dependencies": { 9 | }, 10 | "nexe": { 11 | "input": "index.js", 12 | "output": "test.nex", 13 | "resourceFiles": [ 14 | "hw.txt" 15 | ], 16 | "browserify": { 17 | "excludes": [], 18 | "requires": [] 19 | }, 20 | "temp": "../src", 21 | "debug": false, 22 | "runtime": { 23 | "nodeConfigureOpts": ["--fully-static"], 24 | "framework": "node", 25 | "version": "5.7.0" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | -------------------------------------------------------------------------------- /test/express-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-test", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.13.2", 10 | "cookie-parser": "~1.3.5", 11 | "debug": "~2.2.0", 12 | "express": "~4.13.1", 13 | "jade": "~1.11.0", 14 | "morgan": "~1.6.1", 15 | "serve-favicon": "~2.3.0" 16 | }, 17 | "nexe": { 18 | "input": "bin/www", 19 | "output": "test.nex", 20 | "temp": "../src", 21 | "debug": false, 22 | "runtime": { 23 | "framework": "node", 24 | "version": "5.10.0", 25 | "ignoreFlags": true 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/gulp-test-170/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-test-170", 3 | "version": "0.0.1", 4 | "description": "gulpfile.js issue #170", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Jared Allard ", 10 | "license": "MIT", 11 | "dependencies": { 12 | "gulp": "^3.9.0", 13 | "nexe": "^1.0.5" 14 | }, 15 | "nexe": { 16 | "input": "./index.js", 17 | "output": "test.nex", 18 | "temp": "../src", 19 | "runtime": { 20 | "framework": "node", 21 | "version": "5.7.0", 22 | "ignoreFlags": true, 23 | "node-args": "" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tools/exports.js: -------------------------------------------------------------------------------- 1 | exports["Compressor"] = Compressor; 2 | exports["DefaultsError"] = DefaultsError; 3 | exports["Dictionary"] = Dictionary; 4 | exports["JS_Parse_Error"] = JS_Parse_Error; 5 | exports["MAP"] = MAP; 6 | exports["OutputStream"] = OutputStream; 7 | exports["SourceMap"] = SourceMap; 8 | exports["TreeTransformer"] = TreeTransformer; 9 | exports["TreeWalker"] = TreeWalker; 10 | exports["base54"] = base54; 11 | exports["defaults"] = defaults; 12 | exports["mangle_properties"] = mangle_properties; 13 | exports["merge"] = merge; 14 | exports["parse"] = parse; 15 | exports["push_uniq"] = push_uniq; 16 | exports["string_template"] = string_template; 17 | exports["is_identifier"] = is_identifier; 18 | exports["SymbolDef"] = SymbolDef; 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Craig Condon 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Jixian Wang 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of node-compiler nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /test/express-test/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | var jade = require('jade'); 8 | 9 | var routes = require('./routes/index'); 10 | var users = require('./routes/users'); 11 | 12 | var app = express(); 13 | 14 | // view engine setup 15 | app.set('views', path.join(__dirname, 'views')); 16 | app.set('view engine', 'jade'); 17 | 18 | // uncomment after placing your favicon in /public 19 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 20 | app.use(logger('dev')); 21 | app.use(bodyParser.json()); 22 | app.use(bodyParser.urlencoded({ extended: false })); 23 | app.use(cookieParser()); 24 | app.use(express.static(path.join(__dirname, 'public'))); 25 | 26 | app.use('/', routes); 27 | app.use('/users', users); 28 | 29 | // catch 404 and forward to error handler 30 | app.use(function(req, res, next) { 31 | var err = new Error('Not Found'); 32 | err.status = 404; 33 | next(err); 34 | }); 35 | 36 | // error handlers 37 | 38 | // development error handler 39 | // will print stacktrace 40 | if (app.get('env') === 'development') { 41 | app.use(function(err, req, res, next) { 42 | res.status(err.status || 500); 43 | res.render('error', { 44 | message: err.message, 45 | error: err 46 | }); 47 | }); 48 | } 49 | 50 | // production error handler 51 | // no stacktraces leaked to user 52 | app.use(function(err, req, res, next) { 53 | res.status(err.status || 500); 54 | res.render('error', { 55 | message: err.message, 56 | error: {} 57 | }); 58 | }); 59 | 60 | 61 | module.exports = app; 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Jixian Wang ", 3 | "name": "node-compile", 4 | "description": "It could compile a multi-files node.js project into an executable binary", 5 | "license": "MIT", 6 | "version": "1.2.0", 7 | "contributors": [ 8 | { 9 | "name": "Jixian Wang", 10 | "email": "hellojixian@gmail.com", 11 | "url": "http://shadowgrid.io/" 12 | } 13 | ], 14 | "scripts": { 15 | "test": "echo node: $(node -v) && node_modules/.bin/mocha --reporter mocha-circleci-reporter test/test.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/hellojixian/node-compiler.git" 20 | }, 21 | "main": "./lib/index.js", 22 | "dependencies": { 23 | "async": "^1.5.2", 24 | "babel-preset-es2015": "^6.9.0", 25 | "babel-preset-react": "^6.5.0", 26 | "babelify": "^7.3.0", 27 | "browserify": "^13.0.0", 28 | "colors": "^1.1.2", 29 | "fs-extra": "^0.30.0", 30 | "glob": "^7.0.0", 31 | "gunzip-maybe": "^1.3.1", 32 | "insert-module-globals": "^7.0.1", 33 | "mkdirp": "^0.5.1", 34 | "module-deps": "^4.0.5", 35 | "ncp": "^2.0.0", 36 | "progress": "^1.1.8", 37 | "recursive-readdir-sync": "^1.0.6", 38 | "request": "^2.67.0", 39 | "source-map": "^0.5.6", 40 | "tar-stream": "^1.3.1", 41 | "uglifyify": "^3.0.1", 42 | "yargs": "^4.2.0" 43 | }, 44 | "compiler": { 45 | "input": "./bin/node-compiler", 46 | "output": "compiler^$", 47 | "temp": "src", 48 | "runtime": { 49 | "framework": "nodejs", 50 | "version": "5.5.0", 51 | "ignoreFlags": true 52 | } 53 | }, 54 | "preferGlobal": true, 55 | "bin": { 56 | "node-compiler": "bin/node-compiler" 57 | }, 58 | "devDependencies": { 59 | "mocha": "^2.4.5", 60 | "mocha-circleci-reporter": "0.0.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tools/props.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Node Compiler 2 | 3 | Node Compiler is a command-line utility that compiles your Node.js application into a single executable file. its based on Nexe project [https://github.com/jaredallard/nexe] 4 | 5 | in the production project environment, we found there is a limit about processing the compilicate node.js modules dependency if using nexe, so we decided to fork and extend the project to make it easily to handle large node.js project. 6 | 7 | e.g: today mongodb with node.js is very popular, but by using default nexe, we could not easily produce a working binary which with with monogoose + express.js , well even i know where is the problem, but cannot modify ton of mongodb's driver code, btw, i also like their way to dynamicly load dependencies even its making me some trouble now. 8 | 9 | Actually we found the problem is about intergrate browserify (which to make the whole project into one file ), so we have optimized the config parser, it now much easier to handle *require(variableName)* issue. 10 | 11 | Later one I will explain how exactly it works 12 | 13 | Below part is original from nexe document: 14 | 15 | ### Motivation 16 | 17 | - Ability to run multiple applications with *different* node.js runtimes. 18 | - Distributable binaries without needing node / npm. 19 | - Starts faster. 20 | - Lockdown specific application versions, and easily rollback. 21 | - Faster deployments. 22 | 23 | ## Building Requirements 24 | 25 | - Linux / Mac / BSD / Windows 26 | - Python 2.6 or 2.7 (use --python if not in PATH) 27 | - Windows: Visual Studio 2010+ 28 | 29 | ## Caveats 30 | 31 | ### Doesn't support native modules 32 | 33 | - Use the techniques below for working around dynamic require statements to exclude the module from the bundling, and deploy along side the executable in a node_module folder so your app can find it. Note: On windows you may need to have your app be named node.exe if .node file depends on node. 34 | -------------------------------------------------------------------------------- /test/express-test/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('express-test:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | 92 | process.stdout.write('true'); 93 | process.exit(0); 94 | -------------------------------------------------------------------------------- /lib/log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013 Craig Condon 3 | * Copyright (c) 2015-2016 Jared Allard 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | * 24 | **/ 25 | 26 | var colors = require('colors'); 27 | 28 | /** 29 | * standard output, takes 3 different types. 30 | * log, error, and warn 31 | * 32 | * @param {any} arguments - Text to output. 33 | * @return undefined 34 | **/ 35 | function _log () { 36 | 37 | var args = Array.prototype.slice.call(arguments, 0), 38 | level = args.shift(); 39 | 40 | if (!~["log", "error", "warn"].indexOf(level)) { 41 | args.unshift(level); 42 | level = "log"; 43 | } 44 | 45 | if(level == "log") { 46 | args[0] = "----> " + args[0]; 47 | } else if(level == "error") { 48 | args[0] = "....> " + colors.red("ERROR: ") + args[0] 49 | } else if(level == "warn") { 50 | args[0] = "....> " + colors.yellow("WARNING: ") + args[0] 51 | } 52 | 53 | console[level].apply(console, args); 54 | } 55 | 56 | // export the log function, for require() 57 | module.exports = _log; 58 | -------------------------------------------------------------------------------- /lib/monkeypatch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013 Craig Condon 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | * 23 | **/ 24 | 25 | var async = require("async"), 26 | fs = require("fs"), 27 | _log = require("./log"); 28 | 29 | /** 30 | * Monkey patch a file. 31 | * 32 | * @param {string} filePath - path to file. 33 | * @param {function} monkeyPatched - function to process contents 34 | * @param {function} processor - TODO: detail what this is 35 | * @param {function} complete - callback 36 | * 37 | * @return undefined 38 | */ 39 | function _monkeypatch (filePath, monkeyPatched, processor, complete) { 40 | 41 | async.waterfall([ 42 | 43 | function read (next) { 44 | fs.readFile(filePath, "utf8", next); 45 | }, 46 | 47 | // TODO - need to parse gyp file - this is a bit hacker 48 | function monkeypatch (content, next) { 49 | 50 | if (monkeyPatched(content)) return complete(); 51 | 52 | _log("monkey patch %s", filePath); 53 | processor(content, next); 54 | }, 55 | 56 | function write (content, next) { 57 | fs.writeFile(filePath, content, "utf8", next); 58 | } 59 | ], complete); 60 | } 61 | 62 | // export the function for require() 63 | module.exports = _monkeypatch; 64 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Nexe ChangeLog 2 | 3 | ## 2015-02-20, Version v0.3.7, @jaredallard 4 | 5 | ### Noteable Changes 6 | 7 | * Fixed #103. 8 | * Made not-available require not a fatal error. 9 | * Stub and system to ignore certain requires. 10 | * Added 'sys' to ignore list. 11 | * We have a gitter! 12 | * Gave win32 a 100 length progress bar. 13 | 14 | ### Commits 15 | 16 | * [**2cacd83**] Update README.md (@crcn) 17 | * [**0e90ac9**] Update README.md (@crcn) 18 | * [**54967d1**] Added Gitter badge (@jaredallard) 19 | * [**bb489a3**] Fixes #98 by giving win32 a 100 length progress bar. (@jaredallard) 20 | * [**39665a8**] Lighter weight way to accomplish the exclusion of the sys module (@CKarper) 21 | * [**5aca22d**] This handles embedding 'bin' scripts with shebang interpreter... (@CKarper) 22 | * [**e79b0fb**] Stub to ignore require('sys') (@CKarper) 23 | 24 | ## 2015-02-15, Version v0.3.6, @jaredallard 25 | 26 | ### Noteable Changes 27 | 28 | * Now support .json in require. 29 | * Fixed a major --python flag bug. 30 | 31 | ### Commits 32 | 33 | * [**cac6986**] V bump to solve critical error. (@jaredallard) 34 | * [**b040337**] Fixes #99, resolves #97 by warning on missing file. New examples... (@jaredallard) 35 | * [**ad4da1d**] Support .json extensions in require() resolution (@CKarper) 36 | 37 | ## 2015-02-14, Version v0.3.5, @jaredallard 38 | 39 | 40 | ### Noteable Changes 41 | 42 | * Added new flag: `--python ` 43 | * Added resourceFiles option which allows embedding files and getting them via embed.js 44 | * Updated a bunch of the dependencies. 45 | * `process.argv` is now consistent in a child fork case. 46 | * Added `child_process.fork` support. 47 | * Added new collaborators: 48 | * Jared Allard (@jaredallard) 49 | 50 | ### Commits 51 | 52 | * [**e4155c8**] Version bump 0.3.5, update depends (@jaredallard) 53 | * [**e91f5b5**] Add example, fix outdated examples (@jaredallard) 54 | * [**3b1d5a9**] Modify README, implement cross-plat `--python ` closes #94 (@jaredallard) 55 | * [**29d5f6a**] Make `process.argv` consistent in the child fork case (@LorenzGardner) 56 | * [**97dbd37**] Add support for embedded files. (@LorenzGardner) 57 | * [**b615e12**] Make the example code demonstrate forked process and dynamic require (@LorenzGardner) 58 | * [**333cc69**] update read me mentioning usage of `child_process.fork` (@LorenzGardner) 59 | * [**ece4b2d**] Add `child_process.fork` support. (@LorenzGardner) 60 | -------------------------------------------------------------------------------- /lib/embed.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013 Craig Condon 3 | * Copyright (c) 2015-2016 Jared Allard 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | * 24 | **/ 25 | 26 | 'use strict'; 27 | 28 | let path = require("path"), 29 | _log = require("./log"), 30 | fs = require("fs"); 31 | 32 | /** 33 | * Accessort for embed 34 | **/ 35 | const accessor = function(key) { 36 | if (embeddedFiles.hasOwnProperty(key)) { 37 | return new Buffer(embeddedFiles[key], 'base64'); 38 | } else { 39 | //file was not embedded, throw err. 40 | throw new Error('Embedded file not found'); 41 | } 42 | } 43 | 44 | /** 45 | * Embed files. 46 | * 47 | * @param {array} resourceFiles - array of files to embed. 48 | * @param {string} resourceRoot - root of resources. 49 | * @param {function} compelte - callback 50 | **/ 51 | function embed(resourceFiles, resourceRoot, options, complete) { 52 | 53 | const encode = function(filePath) { 54 | return fs.readFileSync(filePath).toString('base64'); 55 | } 56 | 57 | resourceFiles = resourceFiles || []; 58 | resourceRoot = resourceRoot || ""; 59 | 60 | if (!Array.isArray(resourceFiles)) { 61 | throw new Error("Bad Argument: resourceFiles is not an array"); 62 | } 63 | 64 | let buffer = "var embeddedFiles = {\n"; 65 | for (let i = 0; i < resourceFiles.length; ++i) { 66 | buffer += JSON.stringify(path.relative(resourceRoot, resourceFiles[i])) + ': "'; 67 | buffer += encode(resourceFiles[i]) + '",\n'; 68 | } 69 | 70 | buffer += "\n};\n\nmodule.exports.keys = function () { return Object.keys(embeddedFiles); }\n\nmodule.exports.get = "; 71 | buffer += accessor.toString(); 72 | complete(null, buffer); 73 | } 74 | 75 | module.exports = embed; 76 | -------------------------------------------------------------------------------- /bin/node-compiler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'), 4 | fs = require('fs'), 5 | compiler = require('../lib'), 6 | _log = require("../lib/log"), 7 | cli = require('yargs'). 8 | usage('Usage: $0 -i [sources] -o [binary]'). 9 | options('i', { 10 | demand: true, 11 | alias: 'input', 12 | desc: 'The entry javascript files', 13 | default: process.cwd() 14 | }). 15 | options('o', { 16 | demand: true, 17 | alias: 'output', 18 | desc: 'The output binary', 19 | default: "out.nex" 20 | }). 21 | options('r', { 22 | alias: 'runtime', 23 | default: 'latest', 24 | description: 'The node.js runtime to use' 25 | }). 26 | options('t', { 27 | alias: 'temp', 28 | default: './tmp/compiler', 29 | description: 'The path to store node.js sources' 30 | }). 31 | options('f', { 32 | alias: 'flags', 33 | description: 'Don\'t parse node and v8 flags, pass through app flags', 34 | default: false 35 | }). 36 | options('d', { 37 | alias: 'debug', 38 | default: false, 39 | description: 'generate and use source maps, large file size' 40 | }). 41 | options('j', { 42 | alias: 'jsFlags', 43 | default: false, 44 | description: 'v8 flags for runtime' 45 | }). 46 | options('v', { 47 | alias: 'version', 48 | description: 'Display version number' 49 | }). 50 | options('p', { 51 | alias: 'python', 52 | description: 'Set path of python to use.', 53 | default: 'python' 54 | }). 55 | options('s', { 56 | alias: 'uglify', 57 | description: 'uglify source code', 58 | default: false 59 | }). 60 | options('u', { 61 | alias: 'strip', 62 | description: 'Strip binary', 63 | default: false 64 | }). 65 | options('F', { 66 | alias: 'framework', 67 | description: 'Set the framework to use', 68 | default: 'nodejs' 69 | }); 70 | 71 | var argv = cli.argv; 72 | if (argv.h || argv.help) { 73 | cli.showHelp(); 74 | process.exit(); 75 | } else if (argv.v || argv.version) { 76 | var pkginfo = require('../package.json'); 77 | console.log(pkginfo.version); 78 | process.exit(); 79 | } 80 | 81 | /** 82 | * TODO: Support network shares? 83 | **/ 84 | function toAbsolute(pt) { 85 | if (pt.substr(0, 1) == "/") return pt; // for *nix "/" 86 | if (pt.substr(0, 2) == "\\\\") return pt; // for windows "\\" 87 | if (pt.substr(1, 3) == ":/") return pt; // for windows "c:/" 88 | 89 | // otheerwise... 90 | return path.join(process.cwd(), pt); 91 | } 92 | 93 | if (fs.lstatSync(argv.i).isDirectory()) { 94 | opts = compiler.package(path.join(argv.i, "package.json"), argv); 95 | compiler.compile(opts, function(error) { 96 | if (error) { 97 | return console.log(error.message); 98 | } 99 | }); 100 | } else { 101 | /** 102 | * Call the core 103 | **/ 104 | compiler.compile({ 105 | input: require.resolve(toAbsolute(argv.i)), 106 | output: toAbsolute(argv.o), 107 | flags: argv.f, 108 | nodeVersion: argv.r, 109 | python: argv.python, 110 | nodeTempDir: toAbsolute(argv.t), 111 | framework: argv.F 112 | }, 113 | function(error) { 114 | if (error) { 115 | return console.log(error.message); 116 | } 117 | } 118 | ); 119 | } 120 | -------------------------------------------------------------------------------- /lib/sourcemap.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | 3 | A JavaScript tokenizer / parser / beautifier / compressor. 4 | https://github.com/mishoo/UglifyJS2 5 | 6 | -------------------------------- (C) --------------------------------- 7 | 8 | Author: Mihai Bazon 9 | 10 | http://mihai.bazon.net/blog 11 | 12 | Distributed under the BSD license: 13 | 14 | Copyright 2012 (c) Mihai Bazon 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | * Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer. 23 | 24 | * Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 | SUCH DAMAGE. 41 | 42 | ***********************************************************************/ 43 | 44 | "use strict"; 45 | 46 | // a small wrapper around fitzgen's source-map library 47 | function SourceMap(options) { 48 | options = defaults(options, { 49 | file : null, 50 | root : null, 51 | orig : null, 52 | 53 | orig_line_diff : 0, 54 | dest_line_diff : 0, 55 | }); 56 | var generator = new MOZ_SourceMap.SourceMapGenerator({ 57 | file : options.file, 58 | sourceRoot : options.root 59 | }); 60 | var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig); 61 | function add(source, gen_line, gen_col, orig_line, orig_col, name) { 62 | if (orig_map) { 63 | var info = orig_map.originalPositionFor({ 64 | line: orig_line, 65 | column: orig_col 66 | }); 67 | if (info.source === null) { 68 | return; 69 | } 70 | source = info.source; 71 | orig_line = info.line; 72 | orig_col = info.column; 73 | name = info.name || name; 74 | } 75 | generator.addMapping({ 76 | generated : { line: gen_line + options.dest_line_diff, column: gen_col }, 77 | original : { line: orig_line + options.orig_line_diff, column: orig_col }, 78 | source : source, 79 | name : name 80 | }); 81 | }; 82 | return { 83 | add : add, 84 | get : function() { return generator }, 85 | toString : function() { return JSON.stringify(generator.toJSON()); } 86 | }; 87 | }; 88 | -------------------------------------------------------------------------------- /lib/bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013 Craig Condon 3 | * Copyright (c) 2015-2016 Jared Allard 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | * 24 | **/ 25 | 26 | 'use strict'; 27 | 28 | let browserify = require('browserify'), 29 | path = require("path"), 30 | spawn = require('child_process').spawn, 31 | insertGlobals = require('insert-module-globals'), 32 | fs = require("fs"), 33 | async = require("async"), 34 | UglifyJS = require("../tools/node"), 35 | _log = require("./log"); 36 | ; 37 | 38 | /** 39 | * User browserify to create a "packed" file. 40 | * 41 | * @param {string} input - input file 42 | * @param {string} nc - node compiler dir 43 | * @param {array} options - nexe options 44 | * @param {function} complete - next function to call (async) 45 | **/ 46 | function bundle(input, nc, options, complete) { 47 | const bundlePath = path.join(nc, "lib", "compiled.js"); 48 | const mapfile = options.output+'.map'; 49 | let ws = fs.createWriteStream(bundlePath); 50 | 51 | 52 | const igv = '__filename,__dirname,_process'; 53 | let insertGlobalVars = {}, 54 | wantedGlobalVars = igv.split(','); 55 | 56 | // parse insertGlobalVars. 57 | Object.keys(insertGlobals.vars).forEach(function (x) { 58 | if (wantedGlobalVars.indexOf(x) === -1) { 59 | insertGlobalVars[x] = undefined; 60 | } 61 | }); 62 | 63 | let paths = [path.join(nc, 'lib')]; 64 | 65 | if(options.browserifyPaths) { 66 | paths = paths.concat(options.browserifyPaths); 67 | } 68 | 69 | 70 | _log('executing browserify via API'); 71 | let bproc = browserify([input], { 72 | debug: options.debug, 73 | commondir: false, 74 | paths: paths, 75 | builtins: false, 76 | insertGlobalVars: insertGlobalVars, 77 | detectGlobals: true, 78 | browserField: false 79 | }); 80 | 81 | if (options.browserifyExcludes && Array.isArray(options.browserifyExcludes)) { 82 | for (let i = 0; i < options.browserifyExcludes.length; i++) { 83 | let lib = options.browserifyExcludes[i]; 84 | _log('Excluding \'%s\' from browserify bundle', lib); 85 | bproc.exclude(lib); 86 | } 87 | } 88 | 89 | // copy the excludes code for requires for now. 90 | if (options.browserifyRequires && Array.isArray(options.browserifyRequires)) { 91 | for (let i = 0; i < options.browserifyRequires.length; i++) { 92 | let lib = options.browserifyRequires[i]; 93 | let name = lib.file || lib; // for object format. 94 | 95 | if(typeof lib == 'string') 96 | { 97 | _log('Force including \'%s\' in browserify bundle', name); 98 | bproc.require(lib); 99 | } 100 | else if(typeof lib =='object') 101 | { 102 | if(lib.hasOwnProperty('dir')) 103 | { 104 | // process it as dir 105 | var files = fs.readdirSync(lib.dir); 106 | for(var index in files) { 107 | var file = lib.dir+"/"+files[index]; 108 | var expose = lib.expose_prefix + path.basename(files[index],'.js'); 109 | if(path.extname(files[index])=='.js' && 110 | files[index]!='index.js'){ 111 | _log('Force including \'%s\' in browserify bundle', expose); 112 | bproc.require(file,{ 113 | expose: expose 114 | }); 115 | } 116 | } 117 | } 118 | else 119 | { 120 | // process it as single file 121 | _log('Force including \'%s\' in browserify bundle', lib.expose); 122 | bproc.require(lib.file,{ 123 | expose: lib.expose 124 | }); 125 | } 126 | }else{ 127 | 128 | } 129 | } 130 | } 131 | 132 | if(options.debug) { 133 | bproc.require(require.resolve('source-map-support')) 134 | } 135 | 136 | let bprocbun = bproc 137 | .bundle() // bundle 138 | .pipe(ws) // pipe to file 139 | 140 | // error on require errors, still can't contionue. ffs browserify 141 | bprocbun.on('error', function(err) { 142 | _log('error', '[browserify] '+err); 143 | }); 144 | 145 | ws.on('error', function(err) { 146 | console.log(err); 147 | _log('error', 'Failed to save stdout to disk'); 148 | process.exit(1); 149 | }) 150 | 151 | ws.on('close', function() { 152 | var source = fs.readFileSync(bundlePath, 'utf8'); 153 | source = source.replace(/[^\x00-\x7F]/g, ""); 154 | 155 | if(options.uglify){ 156 | _log("uglify source code"); 157 | var result = UglifyJS.minify(source, {fromString: true}); 158 | source = result.code; 159 | } 160 | 161 | // write the source modified to compiled.js 162 | fs.writeFile(bundlePath, source, 'utf8', function(err) { 163 | if (err) { 164 | _log('error', 'failed to save source'); 165 | process.exit(1); 166 | } 167 | 168 | complete(); 169 | }); 170 | }); 171 | } 172 | 173 | module.exports = bundle; 174 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test runner for nexe 3 | * 4 | * @author Jared Allard 5 | * @license MIT 6 | **/ 7 | 8 | 'use strict'; 9 | 10 | const assert = require('assert'); 11 | const fs = require('fs'); 12 | const path = require('path'); 13 | const spawn = require('child_process').spawn; 14 | 15 | // our vars 16 | const NEXE_VERSION = require('../package.json').version; 17 | const testBase = __dirname; 18 | const LOG_FILE = path.join(testBase, 'test.log'); 19 | 20 | if(fs.existsSync(LOG_FILE)) { 21 | fs.unlinkSync(LOG_FILE); 22 | } 23 | 24 | let logfile = fs.createWriteStream(LOG_FILE, { 25 | autoClose: false 26 | }); 27 | 28 | const compileTest = function(test, cb) { 29 | let testDir = path.join(testBase, test); 30 | if(!fs.existsSync(testDir)) { 31 | throw new Error('Test not found'); 32 | return cb(false); 33 | } 34 | 35 | let npminst = spawn('npm', ['install'], { 36 | cwd: testDir 37 | }); 38 | 39 | npminst.on('error', function() { 40 | throw new Error('Failed to execute NPM'); 41 | return cb(false); 42 | }); 43 | 44 | npminst.on('close', function(code) { 45 | if(code !== 0) { 46 | throw new Error('NPM exited with non-zero status'); 47 | return cb(false); 48 | } 49 | 50 | let testinst = spawn('../../bin/nexe', [], { 51 | cwd: testDir 52 | }); 53 | 54 | testinst.on('error', function() { 55 | throw new Error('Test failed to compile'); 56 | return cb(false); 57 | }); 58 | 59 | testinst.on('close', function(code) { 60 | let err = null; 61 | if(code !== 0) { 62 | throw new Error('Test gave non-zero code'); 63 | err = false; 64 | } 65 | 66 | logfile = fs.createWriteStream(LOG_FILE); 67 | 68 | return cb(err); 69 | }); 70 | 71 | testinst.stdout.on('data', function(d) { 72 | process.stdout.write(d.toString('ascii')); 73 | }); 74 | }); 75 | }; 76 | 77 | /** 78 | * Run a test-generated binary 79 | * 80 | * @param {string} test - test name (dir) 81 | * @param {array} optional args - array of args to pass to the binary 82 | * @param {function} cb - callback 83 | **/ 84 | const runTest = function(test, args, cb) { 85 | let testDir = path.join(testBase, test); 86 | let testBin = path.join(testDir, 'test.nex') 87 | 88 | // check params 89 | if(!Array.isArray(args)) { 90 | cb = args; 91 | args = []; 92 | } 93 | 94 | if(!fs.existsSync(testDir)) { 95 | throw new Error('Test not found'); 96 | return cb(false); 97 | } 98 | 99 | if(!fs.existsSync(testBin)) { 100 | throw new Error('Test binary not found'); 101 | return cb(false); 102 | } 103 | 104 | let returned = false; 105 | let testinst = spawn(testBin, args, { 106 | cwd: testDir 107 | }) 108 | 109 | testinst.stdout.on('data', function(d) { 110 | let status = d.toString('ascii'); 111 | 112 | if(status === true || status === 'true' || status === 'true\n') { 113 | testinst.stdin.pause(); 114 | testinst.kill(); 115 | 116 | // make it know we returned. 117 | returned = true; 118 | 119 | return cb(); 120 | } 121 | }) 122 | 123 | testinst.on('close', function(c) { 124 | if(returned !== true) { 125 | throw new Error('Test failed (stdout never contained true)'); 126 | return cb(false); 127 | } 128 | }); 129 | } 130 | 131 | console.log('NOTICE: The first test may take awhile as it may compile Node.js'); 132 | console.log(' Monitor the log file (test.log) for progress.'); 133 | 134 | 135 | after(function() { 136 | console.log('notice: closing test.log file descriptor.') 137 | logfile.end(); 138 | }); 139 | 140 | /** 141 | * Tests express compatability. 142 | **/ 143 | describe('nexe can bundle express', function() { 144 | let testname = 'express-test'; 145 | 146 | describe('build', function () { 147 | it('compiles without errors', function (next) { 148 | compileTest(testname, function(err) { 149 | return next(err); 150 | }); 151 | }); 152 | 153 | it('runs successfully', function(next) { 154 | runTest(testname, function(err) { 155 | return next(err); 156 | }); 157 | }); 158 | }); 159 | }); 160 | 161 | describe('nexe and embedding files', function() { 162 | let testname = 'embeded-files'; 163 | 164 | describe('build', function () { 165 | it('compiles without errors', function (next) { 166 | compileTest(testname, function(err) { 167 | return next(err); 168 | }); 169 | }); 170 | 171 | it('runs successfully', function(next) { 172 | runTest(testname, function(err) { 173 | return next(err); 174 | }); 175 | }); 176 | }); 177 | }); 178 | 179 | describe('v8 flags test (strict mode)', function() { 180 | let testname = 'flags-test'; 181 | 182 | describe('build', function () { 183 | it('compiles without errors', function (next) { 184 | compileTest(testname, function(err) { 185 | return next(err); 186 | }); 187 | }); 188 | 189 | it('runs successfully', function(next) { 190 | runTest(testname, function(err) { 191 | return next(err); 192 | }); 193 | }); 194 | }); 195 | }); 196 | 197 | describe('nexe can be utilized in gulp test', function() { 198 | let testname = 'gulp-test-170'; 199 | 200 | describe('build', function () { 201 | it('compiles without errors', function (next) { 202 | compileTest(testname, function(err) { 203 | return next(err); 204 | }); 205 | }); 206 | 207 | it('runs successfully', function(next) { 208 | runTest(testname, function(err) { 209 | return next(err); 210 | }); 211 | }); 212 | }); 213 | }); 214 | 215 | describe('nexe can ignore node.js flags test', function() { 216 | let testname = 'ignoreFlags-test'; 217 | 218 | describe('build', function () { 219 | it('compiles without errors', function (next) { 220 | compileTest(testname, function(err) { 221 | return next(err); 222 | }); 223 | }); 224 | 225 | it('runs successfully', function(next) { 226 | runTest(testname, ['--help'], function(err) { 227 | return next(err); 228 | }); 229 | }); 230 | }); 231 | }); 232 | 233 | describe('nexe supports js-yaml test', function() { 234 | let testname = 'js-yaml-148'; 235 | 236 | describe('build', function () { 237 | it('compiles without errors', function (next) { 238 | compileTest(testname, function(err) { 239 | return next(err); 240 | }); 241 | }); 242 | 243 | it('runs successfully', function(next) { 244 | runTest(testname, function(err) { 245 | return next(err); 246 | }); 247 | }); 248 | }); 249 | }); 250 | -------------------------------------------------------------------------------- /lib/transform.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | 3 | A JavaScript tokenizer / parser / beautifier / compressor. 4 | https://github.com/mishoo/UglifyJS2 5 | 6 | -------------------------------- (C) --------------------------------- 7 | 8 | Author: Mihai Bazon 9 | 10 | http://mihai.bazon.net/blog 11 | 12 | Distributed under the BSD license: 13 | 14 | Copyright 2012 (c) Mihai Bazon 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | * Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer. 23 | 24 | * Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 | SUCH DAMAGE. 41 | 42 | ***********************************************************************/ 43 | 44 | "use strict"; 45 | 46 | // Tree transformer helpers. 47 | 48 | function TreeTransformer(before, after) { 49 | TreeWalker.call(this); 50 | this.before = before; 51 | this.after = after; 52 | } 53 | TreeTransformer.prototype = new TreeWalker; 54 | 55 | (function(undefined){ 56 | 57 | function _(node, descend) { 58 | node.DEFMETHOD("transform", function(tw, in_list){ 59 | var x, y; 60 | tw.push(this); 61 | if (tw.before) x = tw.before(this, descend, in_list); 62 | if (x === undefined) { 63 | if (!tw.after) { 64 | x = this; 65 | descend(x, tw); 66 | } else { 67 | tw.stack[tw.stack.length - 1] = x = this; 68 | descend(x, tw); 69 | y = tw.after(x, in_list); 70 | if (y !== undefined) x = y; 71 | } 72 | } 73 | tw.pop(this); 74 | return x; 75 | }); 76 | }; 77 | 78 | function do_list(list, tw) { 79 | return MAP(list, function(node){ 80 | return node.transform(tw, true); 81 | }); 82 | }; 83 | 84 | _(AST_Node, noop); 85 | 86 | _(AST_LabeledStatement, function(self, tw){ 87 | self.label = self.label.transform(tw); 88 | self.body = self.body.transform(tw); 89 | }); 90 | 91 | _(AST_SimpleStatement, function(self, tw){ 92 | self.body = self.body.transform(tw); 93 | }); 94 | 95 | _(AST_Block, function(self, tw){ 96 | self.body = do_list(self.body, tw); 97 | }); 98 | 99 | _(AST_DWLoop, function(self, tw){ 100 | self.condition = self.condition.transform(tw); 101 | self.body = self.body.transform(tw); 102 | }); 103 | 104 | _(AST_For, function(self, tw){ 105 | if (self.init) self.init = self.init.transform(tw); 106 | if (self.condition) self.condition = self.condition.transform(tw); 107 | if (self.step) self.step = self.step.transform(tw); 108 | self.body = self.body.transform(tw); 109 | }); 110 | 111 | _(AST_ForIn, function(self, tw){ 112 | self.init = self.init.transform(tw); 113 | self.object = self.object.transform(tw); 114 | self.body = self.body.transform(tw); 115 | }); 116 | 117 | _(AST_With, function(self, tw){ 118 | self.expression = self.expression.transform(tw); 119 | self.body = self.body.transform(tw); 120 | }); 121 | 122 | _(AST_Exit, function(self, tw){ 123 | if (self.value) self.value = self.value.transform(tw); 124 | }); 125 | 126 | _(AST_LoopControl, function(self, tw){ 127 | if (self.label) self.label = self.label.transform(tw); 128 | }); 129 | 130 | _(AST_If, function(self, tw){ 131 | self.condition = self.condition.transform(tw); 132 | self.body = self.body.transform(tw); 133 | if (self.alternative) self.alternative = self.alternative.transform(tw); 134 | }); 135 | 136 | _(AST_Switch, function(self, tw){ 137 | self.expression = self.expression.transform(tw); 138 | self.body = do_list(self.body, tw); 139 | }); 140 | 141 | _(AST_Case, function(self, tw){ 142 | self.expression = self.expression.transform(tw); 143 | self.body = do_list(self.body, tw); 144 | }); 145 | 146 | _(AST_Try, function(self, tw){ 147 | self.body = do_list(self.body, tw); 148 | if (self.bcatch) self.bcatch = self.bcatch.transform(tw); 149 | if (self.bfinally) self.bfinally = self.bfinally.transform(tw); 150 | }); 151 | 152 | _(AST_Catch, function(self, tw){ 153 | self.argname = self.argname.transform(tw); 154 | self.body = do_list(self.body, tw); 155 | }); 156 | 157 | _(AST_Definitions, function(self, tw){ 158 | self.definitions = do_list(self.definitions, tw); 159 | }); 160 | 161 | _(AST_VarDef, function(self, tw){ 162 | self.name = self.name.transform(tw); 163 | if (self.value) self.value = self.value.transform(tw); 164 | }); 165 | 166 | _(AST_Destructuring, function(self, tw) { 167 | self.names = do_list(self.names, tw); 168 | }); 169 | 170 | _(AST_Lambda, function(self, tw){ 171 | if (self.name) self.name = self.name.transform(tw); 172 | self.argnames = do_list(self.argnames, tw); 173 | if (self.body instanceof AST_Node) { 174 | self.body = self.body.transform(tw); 175 | } else { 176 | self.body = do_list(self.body, tw); 177 | } 178 | }); 179 | 180 | _(AST_Call, function(self, tw){ 181 | self.expression = self.expression.transform(tw); 182 | self.args = do_list(self.args, tw); 183 | }); 184 | 185 | _(AST_Seq, function(self, tw){ 186 | self.car = self.car.transform(tw); 187 | self.cdr = self.cdr.transform(tw); 188 | }); 189 | 190 | _(AST_Dot, function(self, tw){ 191 | self.expression = self.expression.transform(tw); 192 | }); 193 | 194 | _(AST_Sub, function(self, tw){ 195 | self.expression = self.expression.transform(tw); 196 | self.property = self.property.transform(tw); 197 | }); 198 | 199 | _(AST_Unary, function(self, tw){ 200 | self.expression = self.expression.transform(tw); 201 | }); 202 | 203 | _(AST_Binary, function(self, tw){ 204 | self.left = self.left.transform(tw); 205 | self.right = self.right.transform(tw); 206 | }); 207 | 208 | _(AST_Conditional, function(self, tw){ 209 | self.condition = self.condition.transform(tw); 210 | self.consequent = self.consequent.transform(tw); 211 | self.alternative = self.alternative.transform(tw); 212 | }); 213 | 214 | _(AST_Array, function(self, tw){ 215 | self.elements = do_list(self.elements, tw); 216 | }); 217 | 218 | _(AST_Object, function(self, tw){ 219 | self.properties = do_list(self.properties, tw); 220 | }); 221 | 222 | _(AST_ObjectProperty, function(self, tw){ 223 | self.value = self.value.transform(tw); 224 | }); 225 | 226 | })(); 227 | -------------------------------------------------------------------------------- /tools/node.js: -------------------------------------------------------------------------------- 1 | // workaround for tty output truncation upon process.exit() 2 | [process.stdout, process.stderr].forEach(function(stream){ 3 | if (stream._handle && stream._handle.setBlocking) 4 | stream._handle.setBlocking(true); 5 | }); 6 | 7 | var path = require("path"); 8 | var fs = require("fs"); 9 | 10 | var FILES = exports.FILES = [ 11 | "../lib/utils.js", 12 | "../lib/ast.js", 13 | "../lib/parse.js", 14 | "../lib/transform.js", 15 | "../lib/scope.js", 16 | "../lib/output.js", 17 | "../lib/compress.js", 18 | "../lib/sourcemap.js", 19 | "../lib/mozilla-ast.js", 20 | "../lib/propmangle.js", 21 | "./exports.js", 22 | ].map(function(file){ 23 | return fs.realpathSync(path.join(path.dirname(__filename), file)); 24 | }); 25 | 26 | var UglifyJS = exports; 27 | 28 | new Function("MOZ_SourceMap", "exports", FILES.map(function(file){ 29 | return fs.readFileSync(file, "utf8"); 30 | }).join("\n\n"))( 31 | require("source-map"), 32 | UglifyJS 33 | ); 34 | 35 | UglifyJS.AST_Node.warn_function = function(txt) { 36 | console.error("WARN: %s", txt); 37 | }; 38 | 39 | exports.minify = function(files, options) { 40 | options = UglifyJS.defaults(options, { 41 | spidermonkey : false, 42 | outSourceMap : null, 43 | sourceRoot : null, 44 | inSourceMap : null, 45 | fromString : false, 46 | warnings : false, 47 | mangle : {}, 48 | mangleProperties : false, 49 | nameCache : null, 50 | output : null, 51 | compress : {}, 52 | parse : {} 53 | }); 54 | UglifyJS.base54.reset(); 55 | 56 | // 1. parse 57 | var toplevel = null, 58 | sourcesContent = {}; 59 | 60 | if (options.spidermonkey) { 61 | toplevel = UglifyJS.AST_Node.from_mozilla_ast(files); 62 | } else { 63 | if (typeof files == "string") 64 | files = [ files ]; 65 | files.forEach(function(file, i){ 66 | var code = options.fromString 67 | ? file 68 | : fs.readFileSync(file, "utf8"); 69 | sourcesContent[file] = code; 70 | toplevel = UglifyJS.parse(code, { 71 | filename: options.fromString ? i : file, 72 | toplevel: toplevel, 73 | bare_returns: options.parse ? options.parse.bare_returns : undefined 74 | }); 75 | }); 76 | } 77 | if (options.wrap) { 78 | toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll); 79 | } 80 | 81 | // 2. compress 82 | if (options.compress) { 83 | var compress = { warnings: options.warnings }; 84 | UglifyJS.merge(compress, options.compress); 85 | toplevel.figure_out_scope(); 86 | var sq = UglifyJS.Compressor(compress); 87 | toplevel = sq.compress(toplevel); 88 | } 89 | 90 | // 3. mangle properties 91 | if (options.mangleProperties || options.nameCache) { 92 | options.mangleProperties.cache = UglifyJS.readNameCache(options.nameCache, "props"); 93 | toplevel = UglifyJS.mangle_properties(toplevel, options.mangleProperties); 94 | UglifyJS.writeNameCache(options.nameCache, "props", options.mangleProperties.cache); 95 | } 96 | 97 | // 4. mangle 98 | if (options.mangle) { 99 | toplevel.figure_out_scope(options.mangle); 100 | toplevel.compute_char_frequency(options.mangle); 101 | toplevel.mangle_names(options.mangle); 102 | } 103 | 104 | // 5. output 105 | var inMap = options.inSourceMap; 106 | var output = {}; 107 | if (typeof options.inSourceMap == "string") { 108 | inMap = fs.readFileSync(options.inSourceMap, "utf8"); 109 | } 110 | if (options.outSourceMap) { 111 | output.source_map = UglifyJS.SourceMap({ 112 | file: options.outSourceMap, 113 | orig: inMap, 114 | root: options.sourceRoot 115 | }); 116 | if (options.sourceMapIncludeSources) { 117 | for (var file in sourcesContent) { 118 | if (sourcesContent.hasOwnProperty(file)) { 119 | output.source_map.get().setSourceContent(file, sourcesContent[file]); 120 | } 121 | } 122 | } 123 | 124 | } 125 | if (options.output) { 126 | UglifyJS.merge(output, options.output); 127 | } 128 | var stream = UglifyJS.OutputStream(output); 129 | toplevel.print(stream); 130 | 131 | if (options.outSourceMap && "string" === typeof options.outSourceMap) { 132 | stream += "\n//# sourceMappingURL=" + options.outSourceMap; 133 | } 134 | 135 | var source_map = output.source_map; 136 | if (source_map) { 137 | source_map = source_map + ""; 138 | } 139 | 140 | return { 141 | code : stream + "", 142 | map : source_map 143 | }; 144 | }; 145 | 146 | // exports.describe_ast = function() { 147 | // function doitem(ctor) { 148 | // var sub = {}; 149 | // ctor.SUBCLASSES.forEach(function(ctor){ 150 | // sub[ctor.TYPE] = doitem(ctor); 151 | // }); 152 | // var ret = {}; 153 | // if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS; 154 | // if (ctor.SUBCLASSES.length > 0) ret.sub = sub; 155 | // return ret; 156 | // } 157 | // return doitem(UglifyJS.AST_Node).sub; 158 | // } 159 | 160 | exports.describe_ast = function() { 161 | var out = UglifyJS.OutputStream({ beautify: true }); 162 | function doitem(ctor) { 163 | out.print("AST_" + ctor.TYPE); 164 | var props = ctor.SELF_PROPS.filter(function(prop){ 165 | return !/^\$/.test(prop); 166 | }); 167 | if (props.length > 0) { 168 | out.space(); 169 | out.with_parens(function(){ 170 | props.forEach(function(prop, i){ 171 | if (i) out.space(); 172 | out.print(prop); 173 | }); 174 | }); 175 | } 176 | if (ctor.documentation) { 177 | out.space(); 178 | out.print_string(ctor.documentation); 179 | } 180 | if (ctor.SUBCLASSES.length > 0) { 181 | out.space(); 182 | out.with_block(function(){ 183 | ctor.SUBCLASSES.forEach(function(ctor, i){ 184 | out.indent(); 185 | doitem(ctor); 186 | out.newline(); 187 | }); 188 | }); 189 | } 190 | }; 191 | doitem(UglifyJS.AST_Node); 192 | return out + ""; 193 | }; 194 | 195 | function readReservedFile(filename, reserved) { 196 | if (!reserved) { 197 | reserved = { vars: [], props: [] }; 198 | } 199 | var data = fs.readFileSync(filename, "utf8"); 200 | data = JSON.parse(data); 201 | if (data.vars) { 202 | data.vars.forEach(function(name){ 203 | UglifyJS.push_uniq(reserved.vars, name); 204 | }); 205 | } 206 | if (data.props) { 207 | data.props.forEach(function(name){ 208 | UglifyJS.push_uniq(reserved.props, name); 209 | }); 210 | } 211 | return reserved; 212 | } 213 | 214 | exports.readReservedFile = readReservedFile; 215 | 216 | exports.readDefaultReservedFile = function(reserved) { 217 | return readReservedFile(path.join(__dirname, "domprops.json"), reserved); 218 | }; 219 | 220 | exports.readNameCache = function(filename, key) { 221 | var cache = null; 222 | if (filename) { 223 | try { 224 | var cache = fs.readFileSync(filename, "utf8"); 225 | cache = JSON.parse(cache)[key]; 226 | if (!cache) throw "init"; 227 | cache.props = UglifyJS.Dictionary.fromObject(cache.props); 228 | } catch(ex) { 229 | cache = { 230 | cname: -1, 231 | props: new UglifyJS.Dictionary() 232 | }; 233 | } 234 | } 235 | return cache; 236 | }; 237 | 238 | exports.writeNameCache = function(filename, key, cache) { 239 | if (filename) { 240 | var data; 241 | try { 242 | data = fs.readFileSync(filename, "utf8"); 243 | data = JSON.parse(data); 244 | } catch(ex) { 245 | data = {}; 246 | } 247 | data[key] = { 248 | cname: cache.cname, 249 | props: cache.props.toObject() 250 | }; 251 | fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8"); 252 | } 253 | }; 254 | -------------------------------------------------------------------------------- /lib/propmangle.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | 3 | A JavaScript tokenizer / parser / beautifier / compressor. 4 | https://github.com/mishoo/UglifyJS2 5 | 6 | -------------------------------- (C) --------------------------------- 7 | 8 | Author: Mihai Bazon 9 | 10 | http://mihai.bazon.net/blog 11 | 12 | Distributed under the BSD license: 13 | 14 | Copyright 2012 (c) Mihai Bazon 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | * Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer. 23 | 24 | * Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 | SUCH DAMAGE. 41 | 42 | ***********************************************************************/ 43 | 44 | "use strict"; 45 | 46 | function find_builtins() { 47 | 48 | // Compatibility fix for es5.1 and earlier where Symbol isn't defined 49 | if (!global.Symbol) { 50 | global.Symbol = new Function(); 51 | } 52 | 53 | var a = []; 54 | [ Object, Array, Function, Number, 55 | String, Boolean, Error, Math, 56 | Date, RegExp, Symbol 57 | ].forEach(function(ctor){ 58 | Object.getOwnPropertyNames(ctor).map(add); 59 | if (ctor.prototype) { 60 | Object.getOwnPropertyNames(ctor.prototype).map(add); 61 | } 62 | }); 63 | function add(name) { 64 | push_uniq(a, name); 65 | } 66 | return a; 67 | } 68 | 69 | function mangle_properties(ast, options) { 70 | options = defaults(options, { 71 | reserved : null, 72 | cache : null, 73 | only_cache : false, 74 | regex : null 75 | }); 76 | 77 | var reserved = options.reserved; 78 | if (reserved == null) 79 | reserved = find_builtins(); 80 | 81 | var cache = options.cache; 82 | if (cache == null) { 83 | cache = { 84 | cname: -1, 85 | props: new Dictionary() 86 | }; 87 | } 88 | 89 | var regex = options.regex; 90 | 91 | var names_to_mangle = []; 92 | var unmangleable = []; 93 | 94 | // step 1: find candidates to mangle 95 | ast.walk(new TreeWalker(function(node){ 96 | if (node instanceof AST_ObjectKeyVal) { 97 | add(node.key); 98 | } 99 | else if (node instanceof AST_ObjectProperty) { 100 | // setter or getter, since KeyVal is handled above 101 | add(node.key.name); 102 | } 103 | else if (node instanceof AST_Dot) { 104 | if (this.parent() instanceof AST_Assign) { 105 | add(node.property); 106 | } 107 | } 108 | else if (node instanceof AST_Sub) { 109 | if (this.parent() instanceof AST_Assign) { 110 | addStrings(node.property); 111 | } 112 | } 113 | else if (node instanceof AST_ConciseMethod) { 114 | add(node.name.name); 115 | } 116 | })); 117 | 118 | // step 2: transform the tree, renaming properties 119 | return ast.transform(new TreeTransformer(function(node){ 120 | if (node instanceof AST_ObjectKeyVal) { 121 | node.key = mangle(node.key); 122 | } 123 | else if (node instanceof AST_ObjectProperty) { 124 | // setter or getter 125 | node.key.name = mangle(node.key.name); 126 | } 127 | else if (node instanceof AST_Dot) { 128 | node.property = mangle(node.property); 129 | } 130 | else if (node instanceof AST_Sub) { 131 | node.property = mangleStrings(node.property); 132 | } 133 | else if (node instanceof AST_ConciseMethod) { 134 | if (should_mangle(node.name.name)) { 135 | node.name.name = mangle(node.name.name); 136 | } 137 | } 138 | // else if (node instanceof AST_String) { 139 | // if (should_mangle(node.value)) { 140 | // AST_Node.warn( 141 | // "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", { 142 | // file : node.start.file, 143 | // line : node.start.line, 144 | // col : node.start.col, 145 | // prop : node.value 146 | // } 147 | // ); 148 | // } 149 | // } 150 | })); 151 | 152 | // only function declarations after this line 153 | 154 | function can_mangle(name) { 155 | if (unmangleable.indexOf(name) >= 0) return false; 156 | if (reserved.indexOf(name) >= 0) return false; 157 | if (options.only_cache) { 158 | return cache.props.has(name); 159 | } 160 | if (/^[0-9.]+$/.test(name)) return false; 161 | return true; 162 | } 163 | 164 | function should_mangle(name) { 165 | if (regex && !regex.test(name)) return false; 166 | if (reserved.indexOf(name) >= 0) return false; 167 | return cache.props.has(name) 168 | || names_to_mangle.indexOf(name) >= 0; 169 | } 170 | 171 | function add(name) { 172 | if (can_mangle(name)) 173 | push_uniq(names_to_mangle, name); 174 | 175 | if (!should_mangle(name)) { 176 | push_uniq(unmangleable, name); 177 | } 178 | } 179 | 180 | function mangle(name) { 181 | if (!should_mangle(name)) { 182 | return name; 183 | } 184 | 185 | var mangled = cache.props.get(name); 186 | if (!mangled) { 187 | do { 188 | mangled = base54(++cache.cname); 189 | } while (!can_mangle(mangled)); 190 | cache.props.set(name, mangled); 191 | } 192 | return mangled; 193 | } 194 | 195 | function addStrings(node) { 196 | var out = {}; 197 | try { 198 | (function walk(node){ 199 | node.walk(new TreeWalker(function(node){ 200 | if (node instanceof AST_Seq) { 201 | walk(node.cdr); 202 | return true; 203 | } 204 | if (node instanceof AST_String) { 205 | add(node.value); 206 | return true; 207 | } 208 | if (node instanceof AST_Conditional) { 209 | walk(node.consequent); 210 | walk(node.alternative); 211 | return true; 212 | } 213 | throw out; 214 | })); 215 | })(node); 216 | } catch(ex) { 217 | if (ex !== out) throw ex; 218 | } 219 | } 220 | 221 | function mangleStrings(node) { 222 | return node.transform(new TreeTransformer(function(node){ 223 | if (node instanceof AST_Seq) { 224 | node.cdr = mangleStrings(node.cdr); 225 | } 226 | else if (node instanceof AST_String) { 227 | node.value = mangle(node.value); 228 | } 229 | else if (node instanceof AST_Conditional) { 230 | node.consequent = mangleStrings(node.consequent); 231 | node.alternative = mangleStrings(node.alternative); 232 | } 233 | return node; 234 | })); 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | 3 | A JavaScript tokenizer / parser / beautifier / compressor. 4 | https://github.com/mishoo/UglifyJS2 5 | 6 | -------------------------------- (C) --------------------------------- 7 | 8 | Author: Mihai Bazon 9 | 10 | http://mihai.bazon.net/blog 11 | 12 | Distributed under the BSD license: 13 | 14 | Copyright 2012 (c) Mihai Bazon 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | * Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer. 23 | 24 | * Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 | SUCH DAMAGE. 41 | 42 | ***********************************************************************/ 43 | 44 | "use strict"; 45 | 46 | function array_to_hash(a) { 47 | var ret = Object.create(null); 48 | for (var i = 0; i < a.length; ++i) 49 | ret[a[i]] = true; 50 | return ret; 51 | }; 52 | 53 | function slice(a, start) { 54 | return Array.prototype.slice.call(a, start || 0); 55 | }; 56 | 57 | function characters(str) { 58 | return str.split(""); 59 | }; 60 | 61 | function member(name, array) { 62 | return array.indexOf(name) >= 0; 63 | }; 64 | 65 | function find_if(func, array) { 66 | for (var i = 0, n = array.length; i < n; ++i) { 67 | if (func(array[i])) 68 | return array[i]; 69 | } 70 | }; 71 | 72 | function repeat_string(str, i) { 73 | if (i <= 0) return ""; 74 | if (i == 1) return str; 75 | var d = repeat_string(str, i >> 1); 76 | d += d; 77 | if (i & 1) d += str; 78 | return d; 79 | }; 80 | 81 | function DefaultsError(msg, defs) { 82 | Error.call(this, msg); 83 | this.msg = msg; 84 | this.defs = defs; 85 | }; 86 | DefaultsError.prototype = Object.create(Error.prototype); 87 | DefaultsError.prototype.constructor = DefaultsError; 88 | 89 | DefaultsError.croak = function(msg, defs) { 90 | throw new DefaultsError(msg, defs); 91 | }; 92 | 93 | function defaults(args, defs, croak) { 94 | if (args === true) 95 | args = {}; 96 | var ret = args || {}; 97 | if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i)) 98 | DefaultsError.croak("`" + i + "` is not a supported option", defs); 99 | for (var i in defs) if (HOP(defs, i)) { 100 | ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; 101 | } 102 | return ret; 103 | }; 104 | 105 | function merge(obj, ext) { 106 | var count = 0; 107 | for (var i in ext) if (HOP(ext, i)) { 108 | obj[i] = ext[i]; 109 | count++; 110 | } 111 | return count; 112 | }; 113 | 114 | function noop() {}; 115 | 116 | var MAP = (function(){ 117 | function MAP(a, f, backwards) { 118 | var ret = [], top = [], i; 119 | function doit() { 120 | var val = f(a[i], i); 121 | var is_last = val instanceof Last; 122 | if (is_last) val = val.v; 123 | if (val instanceof AtTop) { 124 | val = val.v; 125 | if (val instanceof Splice) { 126 | top.push.apply(top, backwards ? val.v.slice().reverse() : val.v); 127 | } else { 128 | top.push(val); 129 | } 130 | } 131 | else if (val !== skip) { 132 | if (val instanceof Splice) { 133 | ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v); 134 | } else { 135 | ret.push(val); 136 | } 137 | } 138 | return is_last; 139 | }; 140 | if (a instanceof Array) { 141 | if (backwards) { 142 | for (i = a.length; --i >= 0;) if (doit()) break; 143 | ret.reverse(); 144 | top.reverse(); 145 | } else { 146 | for (i = 0; i < a.length; ++i) if (doit()) break; 147 | } 148 | } 149 | else { 150 | for (i in a) if (HOP(a, i)) if (doit()) break; 151 | } 152 | return top.concat(ret); 153 | }; 154 | MAP.at_top = function(val) { return new AtTop(val) }; 155 | MAP.splice = function(val) { return new Splice(val) }; 156 | MAP.last = function(val) { return new Last(val) }; 157 | var skip = MAP.skip = {}; 158 | function AtTop(val) { this.v = val }; 159 | function Splice(val) { this.v = val }; 160 | function Last(val) { this.v = val }; 161 | return MAP; 162 | })(); 163 | 164 | function push_uniq(array, el) { 165 | if (array.indexOf(el) < 0) 166 | array.push(el); 167 | }; 168 | 169 | function string_template(text, props) { 170 | return text.replace(/\{(.+?)\}/g, function(str, p){ 171 | return props[p]; 172 | }); 173 | }; 174 | 175 | function remove(array, el) { 176 | for (var i = array.length; --i >= 0;) { 177 | if (array[i] === el) array.splice(i, 1); 178 | } 179 | }; 180 | 181 | function mergeSort(array, cmp) { 182 | if (array.length < 2) return array.slice(); 183 | function merge(a, b) { 184 | var r = [], ai = 0, bi = 0, i = 0; 185 | while (ai < a.length && bi < b.length) { 186 | cmp(a[ai], b[bi]) <= 0 187 | ? r[i++] = a[ai++] 188 | : r[i++] = b[bi++]; 189 | } 190 | if (ai < a.length) r.push.apply(r, a.slice(ai)); 191 | if (bi < b.length) r.push.apply(r, b.slice(bi)); 192 | return r; 193 | }; 194 | function _ms(a) { 195 | if (a.length <= 1) 196 | return a; 197 | var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m); 198 | left = _ms(left); 199 | right = _ms(right); 200 | return merge(left, right); 201 | }; 202 | return _ms(array); 203 | }; 204 | 205 | function set_difference(a, b) { 206 | return a.filter(function(el){ 207 | return b.indexOf(el) < 0; 208 | }); 209 | }; 210 | 211 | function set_intersection(a, b) { 212 | return a.filter(function(el){ 213 | return b.indexOf(el) >= 0; 214 | }); 215 | }; 216 | 217 | // this function is taken from Acorn [1], written by Marijn Haverbeke 218 | // [1] https://github.com/marijnh/acorn 219 | function makePredicate(words) { 220 | if (!(words instanceof Array)) words = words.split(" "); 221 | var f = "", cats = []; 222 | out: for (var i = 0; i < words.length; ++i) { 223 | for (var j = 0; j < cats.length; ++j) 224 | if (cats[j][0].length == words[i].length) { 225 | cats[j].push(words[i]); 226 | continue out; 227 | } 228 | cats.push([words[i]]); 229 | } 230 | function compareTo(arr) { 231 | if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"; 232 | f += "switch(str){"; 233 | for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"; 234 | f += "return true}return false;"; 235 | } 236 | // When there are more than three length categories, an outer 237 | // switch first dispatches on the lengths, to save on comparisons. 238 | if (cats.length > 3) { 239 | cats.sort(function(a, b) {return b.length - a.length;}); 240 | f += "switch(str.length){"; 241 | for (var i = 0; i < cats.length; ++i) { 242 | var cat = cats[i]; 243 | f += "case " + cat[0].length + ":"; 244 | compareTo(cat); 245 | } 246 | f += "}"; 247 | // Otherwise, simply generate a flat `switch` statement. 248 | } else { 249 | compareTo(words); 250 | } 251 | return new Function("str", f); 252 | }; 253 | 254 | function all(array, predicate) { 255 | for (var i = array.length; --i >= 0;) 256 | if (!predicate(array[i])) 257 | return false; 258 | return true; 259 | }; 260 | 261 | function Dictionary() { 262 | this._values = Object.create(null); 263 | this._size = 0; 264 | }; 265 | Dictionary.prototype = { 266 | set: function(key, val) { 267 | if (!this.has(key)) ++this._size; 268 | this._values["$" + key] = val; 269 | return this; 270 | }, 271 | add: function(key, val) { 272 | if (this.has(key)) { 273 | this.get(key).push(val); 274 | } else { 275 | this.set(key, [ val ]); 276 | } 277 | return this; 278 | }, 279 | get: function(key) { return this._values["$" + key] }, 280 | del: function(key) { 281 | if (this.has(key)) { 282 | --this._size; 283 | delete this._values["$" + key]; 284 | } 285 | return this; 286 | }, 287 | has: function(key) { return ("$" + key) in this._values }, 288 | each: function(f) { 289 | for (var i in this._values) 290 | f(this._values[i], i.substr(1)); 291 | }, 292 | size: function() { 293 | return this._size; 294 | }, 295 | map: function(f) { 296 | var ret = []; 297 | for (var i in this._values) 298 | ret.push(f(this._values[i], i.substr(1))); 299 | return ret; 300 | }, 301 | toObject: function() { return this._values } 302 | }; 303 | Dictionary.fromObject = function(obj) { 304 | var dict = new Dictionary(); 305 | dict._size = merge(dict._values, obj); 306 | return dict; 307 | }; 308 | 309 | function HOP(obj, prop) { 310 | return Object.prototype.hasOwnProperty.call(obj, prop); 311 | } 312 | -------------------------------------------------------------------------------- /lib/mozilla-ast.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | 3 | A JavaScript tokenizer / parser / beautifier / compressor. 4 | https://github.com/mishoo/UglifyJS2 5 | 6 | -------------------------------- (C) --------------------------------- 7 | 8 | Author: Mihai Bazon 9 | 10 | http://mihai.bazon.net/blog 11 | 12 | Distributed under the BSD license: 13 | 14 | Copyright 2012 (c) Mihai Bazon 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | * Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer. 23 | 24 | * Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 | SUCH DAMAGE. 41 | 42 | ***********************************************************************/ 43 | 44 | "use strict"; 45 | 46 | (function(){ 47 | 48 | var MOZ_TO_ME = { 49 | ExpressionStatement: function(M) { 50 | var expr = M.expression; 51 | if (expr.type === "Literal" && typeof expr.value === "string") { 52 | return new AST_Directive({ 53 | start: my_start_token(M), 54 | end: my_end_token(M), 55 | value: expr.value 56 | }); 57 | } 58 | return new AST_SimpleStatement({ 59 | start: my_start_token(M), 60 | end: my_end_token(M), 61 | body: from_moz(expr) 62 | }); 63 | }, 64 | TryStatement: function(M) { 65 | var handlers = M.handlers || [M.handler]; 66 | if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) { 67 | throw new Error("Multiple catch clauses are not supported."); 68 | } 69 | return new AST_Try({ 70 | start : my_start_token(M), 71 | end : my_end_token(M), 72 | body : from_moz(M.block).body, 73 | bcatch : from_moz(handlers[0]), 74 | bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null 75 | }); 76 | }, 77 | Property: function(M) { 78 | var key = M.key; 79 | var name = key.type == "Identifier" ? key.name : key.value; 80 | var args = { 81 | start : my_start_token(key), 82 | end : my_end_token(M.value), 83 | key : name, 84 | value : from_moz(M.value) 85 | }; 86 | switch (M.kind) { 87 | case "init": 88 | return new AST_ObjectKeyVal(args); 89 | case "set": 90 | args.value.name = from_moz(key); 91 | return new AST_ObjectSetter(args); 92 | case "get": 93 | args.value.name = from_moz(key); 94 | return new AST_ObjectGetter(args); 95 | } 96 | }, 97 | ObjectExpression: function(M) { 98 | return new AST_Object({ 99 | start : my_start_token(M), 100 | end : my_end_token(M), 101 | properties : M.properties.map(function(prop){ 102 | prop.type = "Property"; 103 | return from_moz(prop) 104 | }) 105 | }); 106 | }, 107 | SequenceExpression: function(M) { 108 | return AST_Seq.from_array(M.expressions.map(from_moz)); 109 | }, 110 | MemberExpression: function(M) { 111 | return new (M.computed ? AST_Sub : AST_Dot)({ 112 | start : my_start_token(M), 113 | end : my_end_token(M), 114 | property : M.computed ? from_moz(M.property) : M.property.name, 115 | expression : from_moz(M.object) 116 | }); 117 | }, 118 | SwitchCase: function(M) { 119 | return new (M.test ? AST_Case : AST_Default)({ 120 | start : my_start_token(M), 121 | end : my_end_token(M), 122 | expression : from_moz(M.test), 123 | body : M.consequent.map(from_moz) 124 | }); 125 | }, 126 | VariableDeclaration: function(M) { 127 | return new (M.kind === "const" ? AST_Const : AST_Var)({ 128 | start : my_start_token(M), 129 | end : my_end_token(M), 130 | definitions : M.declarations.map(from_moz) 131 | }); 132 | }, 133 | Literal: function(M) { 134 | var val = M.value, args = { 135 | start : my_start_token(M), 136 | end : my_end_token(M) 137 | }; 138 | if (val === null) return new AST_Null(args); 139 | switch (typeof val) { 140 | case "string": 141 | args.value = val; 142 | return new AST_String(args); 143 | case "number": 144 | args.value = val; 145 | return new AST_Number(args); 146 | case "boolean": 147 | return new (val ? AST_True : AST_False)(args); 148 | default: 149 | var rx = M.regex; 150 | if (rx && rx.pattern) { 151 | // RegExpLiteral as per ESTree AST spec 152 | args.value = new RegExp(rx.pattern, rx.flags).toString(); 153 | } else { 154 | // support legacy RegExp 155 | args.value = M.regex && M.raw ? M.raw : val; 156 | } 157 | return new AST_RegExp(args); 158 | } 159 | }, 160 | Identifier: function(M) { 161 | var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; 162 | return new ( p.type == "LabeledStatement" ? AST_Label 163 | : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar) 164 | : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg) 165 | : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg) 166 | : p.type == "CatchClause" ? AST_SymbolCatch 167 | : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef 168 | : AST_SymbolRef)({ 169 | start : my_start_token(M), 170 | end : my_end_token(M), 171 | name : M.name 172 | }); 173 | } 174 | }; 175 | 176 | MOZ_TO_ME.UpdateExpression = 177 | MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) { 178 | var prefix = "prefix" in M ? M.prefix 179 | : M.type == "UnaryExpression" ? true : false; 180 | return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({ 181 | start : my_start_token(M), 182 | end : my_end_token(M), 183 | operator : M.operator, 184 | expression : from_moz(M.argument) 185 | }); 186 | }; 187 | 188 | map("Program", AST_Toplevel, "body@body"); 189 | map("EmptyStatement", AST_EmptyStatement); 190 | map("BlockStatement", AST_BlockStatement, "body@body"); 191 | map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative"); 192 | map("LabeledStatement", AST_LabeledStatement, "label>label, body>body"); 193 | map("BreakStatement", AST_Break, "label>label"); 194 | map("ContinueStatement", AST_Continue, "label>label"); 195 | map("WithStatement", AST_With, "object>expression, body>body"); 196 | map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body"); 197 | map("ReturnStatement", AST_Return, "argument>value"); 198 | map("ThrowStatement", AST_Throw, "argument>value"); 199 | map("WhileStatement", AST_While, "test>condition, body>body"); 200 | map("DoWhileStatement", AST_Do, "test>condition, body>body"); 201 | map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body"); 202 | map("ForInStatement", AST_ForIn, "left>init, right>object, body>body"); 203 | map("DebuggerStatement", AST_Debugger); 204 | map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body"); 205 | map("VariableDeclarator", AST_VarDef, "id>name, init>value"); 206 | map("CatchClause", AST_Catch, "param>argname, body%body"); 207 | 208 | map("ThisExpression", AST_This); 209 | map("ArrayExpression", AST_Array, "elements@elements"); 210 | map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body"); 211 | map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right"); 212 | map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right"); 213 | map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right"); 214 | map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative"); 215 | map("NewExpression", AST_New, "callee>expression, arguments@args"); 216 | map("CallExpression", AST_Call, "callee>expression, arguments@args"); 217 | 218 | def_to_moz(AST_Directive, function To_Moz_Directive(M) { 219 | return { 220 | type: "ExpressionStatement", 221 | expression: { 222 | type: "Literal", 223 | value: M.value 224 | } 225 | }; 226 | }); 227 | 228 | def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) { 229 | return { 230 | type: "ExpressionStatement", 231 | expression: to_moz(M.body) 232 | }; 233 | }); 234 | 235 | def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) { 236 | return { 237 | type: "SwitchCase", 238 | test: to_moz(M.expression), 239 | consequent: M.body.map(to_moz) 240 | }; 241 | }); 242 | 243 | def_to_moz(AST_Try, function To_Moz_TryStatement(M) { 244 | return { 245 | type: "TryStatement", 246 | block: to_moz_block(M), 247 | handler: to_moz(M.bcatch), 248 | guardedHandlers: [], 249 | finalizer: to_moz(M.bfinally) 250 | }; 251 | }); 252 | 253 | def_to_moz(AST_Catch, function To_Moz_CatchClause(M) { 254 | return { 255 | type: "CatchClause", 256 | param: to_moz(M.argname), 257 | guard: null, 258 | body: to_moz_block(M) 259 | }; 260 | }); 261 | 262 | def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) { 263 | return { 264 | type: "VariableDeclaration", 265 | kind: M instanceof AST_Const ? "const" : "var", 266 | declarations: M.definitions.map(to_moz) 267 | }; 268 | }); 269 | 270 | def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) { 271 | return { 272 | type: "SequenceExpression", 273 | expressions: M.to_array().map(to_moz) 274 | }; 275 | }); 276 | 277 | def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) { 278 | var isComputed = M instanceof AST_Sub; 279 | return { 280 | type: "MemberExpression", 281 | object: to_moz(M.expression), 282 | computed: isComputed, 283 | property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property} 284 | }; 285 | }); 286 | 287 | def_to_moz(AST_Unary, function To_Moz_Unary(M) { 288 | return { 289 | type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression", 290 | operator: M.operator, 291 | prefix: M instanceof AST_UnaryPrefix, 292 | argument: to_moz(M.expression) 293 | }; 294 | }); 295 | 296 | def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) { 297 | return { 298 | type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression", 299 | left: to_moz(M.left), 300 | operator: M.operator, 301 | right: to_moz(M.right) 302 | }; 303 | }); 304 | 305 | def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) { 306 | return { 307 | type: "ObjectExpression", 308 | properties: M.properties.map(to_moz) 309 | }; 310 | }); 311 | 312 | def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) { 313 | var key = ( 314 | is_identifier(M.key) 315 | ? {type: "Identifier", name: M.key} 316 | : {type: "Literal", value: M.key} 317 | ); 318 | var kind; 319 | if (M instanceof AST_ObjectKeyVal) { 320 | kind = "init"; 321 | } else 322 | if (M instanceof AST_ObjectGetter) { 323 | kind = "get"; 324 | } else 325 | if (M instanceof AST_ObjectSetter) { 326 | kind = "set"; 327 | } 328 | return { 329 | type: "Property", 330 | kind: kind, 331 | key: key, 332 | value: to_moz(M.value) 333 | }; 334 | }); 335 | 336 | def_to_moz(AST_Symbol, function To_Moz_Identifier(M) { 337 | var def = M.definition(); 338 | return { 339 | type: "Identifier", 340 | name: def ? def.mangled_name || def.name : M.name 341 | }; 342 | }); 343 | 344 | def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) { 345 | var value = M.value; 346 | return { 347 | type: "Literal", 348 | value: value, 349 | raw: value.toString(), 350 | regex: { 351 | pattern: value.source, 352 | flags: value.toString().match(/[gimuy]*$/)[0] 353 | } 354 | }; 355 | }); 356 | 357 | def_to_moz(AST_Constant, function To_Moz_Literal(M) { 358 | var value = M.value; 359 | if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) { 360 | return { 361 | type: "UnaryExpression", 362 | operator: "-", 363 | prefix: true, 364 | argument: { 365 | type: "Literal", 366 | value: -value, 367 | raw: M.start.raw 368 | } 369 | }; 370 | } 371 | return { 372 | type: "Literal", 373 | value: value, 374 | raw: M.start.raw 375 | }; 376 | }); 377 | 378 | def_to_moz(AST_Atom, function To_Moz_Atom(M) { 379 | return { 380 | type: "Identifier", 381 | name: String(M.value) 382 | }; 383 | }); 384 | 385 | AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); 386 | AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); 387 | AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null }); 388 | 389 | AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast); 390 | AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast); 391 | 392 | /* -----[ tools ]----- */ 393 | 394 | function raw_token(moznode) { 395 | if (moznode.type == "Literal") { 396 | return moznode.raw != null ? moznode.raw : moznode.value + ""; 397 | } 398 | } 399 | 400 | function my_start_token(moznode) { 401 | var loc = moznode.loc, start = loc && loc.start; 402 | var range = moznode.range; 403 | return new AST_Token({ 404 | file : loc && loc.source, 405 | line : start && start.line, 406 | col : start && start.column, 407 | pos : range ? range[0] : moznode.start, 408 | endline : start && start.line, 409 | endcol : start && start.column, 410 | endpos : range ? range[0] : moznode.start, 411 | raw : raw_token(moznode), 412 | }); 413 | }; 414 | 415 | function my_end_token(moznode) { 416 | var loc = moznode.loc, end = loc && loc.end; 417 | var range = moznode.range; 418 | return new AST_Token({ 419 | file : loc && loc.source, 420 | line : end && end.line, 421 | col : end && end.column, 422 | pos : range ? range[1] : moznode.end, 423 | endline : end && end.line, 424 | endcol : end && end.column, 425 | endpos : range ? range[1] : moznode.end, 426 | raw : raw_token(moznode), 427 | }); 428 | }; 429 | 430 | function map(moztype, mytype, propmap) { 431 | var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; 432 | moz_to_me += "return new U2." + mytype.name + "({\n" + 433 | "start: my_start_token(M),\n" + 434 | "end: my_end_token(M)"; 435 | 436 | var me_to_moz = "function To_Moz_" + moztype + "(M){\n"; 437 | me_to_moz += "return {\n" + 438 | "type: " + JSON.stringify(moztype); 439 | 440 | if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){ 441 | var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop); 442 | if (!m) throw new Error("Can't understand property map: " + prop); 443 | var moz = m[1], how = m[2], my = m[3]; 444 | moz_to_me += ",\n" + my + ": "; 445 | me_to_moz += ",\n" + moz + ": "; 446 | switch (how) { 447 | case "@": 448 | moz_to_me += "M." + moz + ".map(from_moz)"; 449 | me_to_moz += "M." + my + ".map(to_moz)"; 450 | break; 451 | case ">": 452 | moz_to_me += "from_moz(M." + moz + ")"; 453 | me_to_moz += "to_moz(M." + my + ")"; 454 | break; 455 | case "=": 456 | moz_to_me += "M." + moz; 457 | me_to_moz += "M." + my; 458 | break; 459 | case "%": 460 | moz_to_me += "from_moz(M." + moz + ").body"; 461 | me_to_moz += "to_moz_block(M)"; 462 | break; 463 | default: 464 | throw new Error("Can't understand operator in propmap: " + prop); 465 | } 466 | }); 467 | 468 | moz_to_me += "\n})\n}"; 469 | me_to_moz += "\n}\n}"; 470 | 471 | //moz_to_me = parse(moz_to_me).print_to_string({ beautify: true }); 472 | //me_to_moz = parse(me_to_moz).print_to_string({ beautify: true }); 473 | //console.log(moz_to_me); 474 | 475 | moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")( 476 | exports, my_start_token, my_end_token, from_moz 477 | ); 478 | me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")( 479 | to_moz, to_moz_block 480 | ); 481 | MOZ_TO_ME[moztype] = moz_to_me; 482 | def_to_moz(mytype, me_to_moz); 483 | }; 484 | 485 | var FROM_MOZ_STACK = null; 486 | 487 | function from_moz(node) { 488 | FROM_MOZ_STACK.push(node); 489 | var ret = node != null ? MOZ_TO_ME[node.type](node) : null; 490 | FROM_MOZ_STACK.pop(); 491 | return ret; 492 | }; 493 | 494 | AST_Node.from_mozilla_ast = function(node){ 495 | var save_stack = FROM_MOZ_STACK; 496 | FROM_MOZ_STACK = []; 497 | var ast = from_moz(node); 498 | FROM_MOZ_STACK = save_stack; 499 | return ast; 500 | }; 501 | 502 | function set_moz_loc(mynode, moznode, myparent) { 503 | var start = mynode.start; 504 | var end = mynode.end; 505 | if (start.pos != null && end.endpos != null) { 506 | moznode.range = [start.pos, end.endpos]; 507 | } 508 | if (start.line) { 509 | moznode.loc = { 510 | start: {line: start.line, column: start.col}, 511 | end: end.endline ? {line: end.endline, column: end.endcol} : null 512 | }; 513 | if (start.file) { 514 | moznode.loc.source = start.file; 515 | } 516 | } 517 | return moznode; 518 | }; 519 | 520 | function def_to_moz(mytype, handler) { 521 | mytype.DEFMETHOD("to_mozilla_ast", function() { 522 | return set_moz_loc(this, handler(this)); 523 | }); 524 | }; 525 | 526 | function to_moz(node) { 527 | return node != null ? node.to_mozilla_ast() : null; 528 | }; 529 | 530 | function to_moz_block(node) { 531 | return { 532 | type: "BlockStatement", 533 | body: node.body.map(to_moz) 534 | }; 535 | }; 536 | 537 | })(); 538 | -------------------------------------------------------------------------------- /lib/scope.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | 3 | A JavaScript tokenizer / parser / beautifier / compressor. 4 | https://github.com/mishoo/UglifyJS2 5 | 6 | -------------------------------- (C) --------------------------------- 7 | 8 | Author: Mihai Bazon 9 | 10 | http://mihai.bazon.net/blog 11 | 12 | Distributed under the BSD license: 13 | 14 | Copyright 2012 (c) Mihai Bazon 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | * Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer. 23 | 24 | * Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 | SUCH DAMAGE. 41 | 42 | ***********************************************************************/ 43 | 44 | "use strict"; 45 | 46 | function SymbolDef(scope, index, orig) { 47 | this.name = orig.name; 48 | this.orig = [ orig ]; 49 | this.scope = scope; 50 | this.references = []; 51 | this.global = false; 52 | this.export = false; 53 | this.mangled_name = null; 54 | this.object_destructuring_arg = false; 55 | this.undeclared = false; 56 | this.constant = false; 57 | this.index = index; 58 | this.id = SymbolDef.next_id++; 59 | }; 60 | 61 | SymbolDef.next_id = 1; 62 | 63 | SymbolDef.prototype = { 64 | unmangleable: function(options) { 65 | if (!options) options = {}; 66 | 67 | return (this.global && !options.toplevel) 68 | || this.export 69 | || this.object_destructuring_arg 70 | || this.undeclared 71 | || (!options.eval && (this.scope.uses_eval || this.scope.uses_with)) 72 | || (options.keep_fnames 73 | && (this.orig[0] instanceof AST_SymbolLambda 74 | || this.orig[0] instanceof AST_SymbolDefun)) 75 | || this.orig[0] instanceof AST_SymbolMethod 76 | || (options.keep_classnames 77 | && (this.orig[0] instanceof AST_SymbolClass 78 | || this.orig[0] instanceof AST_SymbolDefClass)); 79 | }, 80 | mangle: function(options) { 81 | var cache = options.cache && options.cache.props; 82 | if (this.global && cache && cache.has(this.name)) { 83 | this.mangled_name = cache.get(this.name); 84 | } 85 | else if (!this.mangled_name && !this.unmangleable(options)) { 86 | var s = this.scope; 87 | if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda) 88 | s = s.parent_scope; 89 | this.mangled_name = s.next_mangled(options, this); 90 | if (this.global && cache) { 91 | cache.set(this.name, this.mangled_name); 92 | } 93 | } 94 | } 95 | }; 96 | 97 | AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ 98 | options = defaults(options, { 99 | screw_ie8: false, 100 | cache: null 101 | }); 102 | 103 | // pass 1: setup scope chaining and handle definitions 104 | var self = this; 105 | var scope = self.parent_scope = null; 106 | var labels = new Dictionary(); 107 | var defun = null; 108 | var last_var_had_const_pragma = false; 109 | var nesting = 0; 110 | var in_destructuring = null; 111 | var in_export; 112 | var tw = new TreeWalker(function(node, descend){ 113 | var create_a_block_scope = 114 | (options.screw_ie8 && node instanceof AST_Catch) || 115 | ((node instanceof AST_Block) && node.creates_block_scope()); 116 | if (create_a_block_scope) { 117 | var save_scope = scope; 118 | scope = new AST_Scope(node); 119 | scope.init_scope_vars(nesting); 120 | scope.parent_scope = save_scope; 121 | scope.is_block_scope = true; 122 | descend(); 123 | scope = save_scope; 124 | return true; 125 | } 126 | if (node instanceof AST_Destructuring && node.is_array === false) { 127 | in_destructuring = node; // These don't nest 128 | descend(); 129 | in_destructuring = null; 130 | return true; 131 | } 132 | if (node instanceof AST_Scope) { 133 | node.init_scope_vars(nesting); 134 | var save_scope = node.parent_scope = scope; 135 | var save_defun = defun; 136 | var save_labels = labels; 137 | defun = scope = node; 138 | labels = new Dictionary(); 139 | ++nesting; descend(); --nesting; 140 | scope = save_scope; 141 | defun = save_defun; 142 | labels = save_labels; 143 | return true; // don't descend again in TreeWalker 144 | } 145 | if (node instanceof AST_Export) { 146 | in_export = true; 147 | descend(); 148 | in_export = false; 149 | } 150 | if (node instanceof AST_LabeledStatement) { 151 | var l = node.label; 152 | if (labels.has(l.name)) { 153 | throw new Error(string_template("Label {name} defined twice", l)); 154 | } 155 | labels.set(l.name, l); 156 | descend(); 157 | labels.del(l.name); 158 | return true; // no descend again 159 | } 160 | if (node instanceof AST_With) { 161 | for (var s = scope; s; s = s.parent_scope) 162 | s.uses_with = true; 163 | return; 164 | } 165 | if (node instanceof AST_Symbol) { 166 | node.scope = scope; 167 | } 168 | if (node instanceof AST_SymbolFunarg) { 169 | node.object_destructuring_arg = !!in_destructuring; 170 | defun.def_variable(node, in_export); 171 | } 172 | if (node instanceof AST_Label) { 173 | node.thedef = node; 174 | node.references = []; 175 | } 176 | if (node instanceof AST_SymbolLambda) { 177 | defun.def_function(node, in_export); 178 | } 179 | else if (node instanceof AST_SymbolDefun) { 180 | // Careful here, the scope where this should be defined is 181 | // the parent scope. The reason is that we enter a new 182 | // scope when we encounter the AST_Defun node (which is 183 | // instanceof AST_Scope) but we get to the symbol a bit 184 | // later. 185 | var parent_lambda = defun.parent_scope; 186 | while (parent_lambda.is_block_scope) { 187 | parent_lambda = parent_lambda.parent_scope; 188 | } 189 | (node.scope = parent_lambda).def_function(node, in_export); 190 | } 191 | else if (node instanceof AST_SymbolClass) { 192 | defun.def_variable(node, in_export); 193 | } 194 | else if (node instanceof AST_SymbolImport) { 195 | scope.def_variable(node, in_export); 196 | } 197 | else if (node instanceof AST_SymbolDefClass) { 198 | // This deals with the name of the class being available 199 | // inside the class. 200 | (node.scope = defun.parent_scope).def_function(node, in_export); 201 | } 202 | else if (node instanceof AST_Var) { 203 | last_var_had_const_pragma = node.has_const_pragma(); 204 | } 205 | else if (node instanceof AST_SymbolVar 206 | || node instanceof AST_SymbolConst 207 | || node instanceof AST_SymbolLet) { 208 | var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node, in_export); 209 | def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma; 210 | def.destructuring = in_destructuring; 211 | def.init = tw.parent().value; 212 | } 213 | else if (node instanceof AST_SymbolCatch) { 214 | (options.screw_ie8 ? scope : defun) 215 | .def_variable(node); 216 | } 217 | else if (node instanceof AST_LabelRef) { 218 | var sym = labels.get(node.name); 219 | if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", { 220 | name: node.name, 221 | line: node.start.line, 222 | col: node.start.col 223 | })); 224 | node.thedef = sym; 225 | } 226 | }); 227 | self.walk(tw); 228 | 229 | // pass 2: find back references and eval 230 | var func = null; 231 | var cls = null; 232 | var globals = self.globals = new Dictionary(); 233 | var tw = new TreeWalker(function(node, descend){ 234 | if (node instanceof AST_Lambda) { 235 | var prev_func = func; 236 | func = node; 237 | descend(); 238 | func = prev_func; 239 | return true; 240 | } 241 | if (node instanceof AST_Class) { 242 | var prev_cls = cls; 243 | cls = node; 244 | descend(); 245 | cls = prev_cls; 246 | return true; 247 | } 248 | if (node instanceof AST_LoopControl && node.label) { 249 | node.label.thedef.references.push(node); 250 | return true; 251 | } 252 | if (node instanceof AST_SymbolRef) { 253 | var name = node.name; 254 | if (name == "eval" && tw.parent() instanceof AST_Call) { 255 | for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { 256 | s.uses_eval = true; 257 | } 258 | } 259 | var sym = node.scope.find_variable(name); 260 | if (!sym) { 261 | var g; 262 | if (globals.has(name)) { 263 | g = globals.get(name); 264 | } else { 265 | g = new SymbolDef(self, globals.size(), node); 266 | g.undeclared = true; 267 | g.global = true; 268 | globals.set(name, g); 269 | } 270 | node.thedef = g; 271 | if (func && name == "arguments") { 272 | func.uses_arguments = true; 273 | } 274 | } else { 275 | node.thedef = sym; 276 | } 277 | node.reference(); 278 | return true; 279 | } 280 | }); 281 | self.walk(tw); 282 | 283 | if (options.cache) { 284 | this.cname = options.cache.cname; 285 | } 286 | }); 287 | 288 | AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){ 289 | this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) 290 | this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) 291 | this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement 292 | this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` 293 | this.parent_scope = null; // the parent scope 294 | this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes 295 | this.cname = -1; // the current index for mangling functions/variables 296 | this.nesting = nesting; // the nesting level of this scope (0 means toplevel) 297 | }); 298 | 299 | AST_Block.DEFMETHOD("creates_block_scope", function() { 300 | return ( 301 | !(this instanceof AST_Lambda) && 302 | !(this instanceof AST_Toplevel) && 303 | !(this instanceof AST_Class) 304 | ); 305 | }); 306 | 307 | AST_Lambda.DEFMETHOD("init_scope_vars", function(){ 308 | AST_Scope.prototype.init_scope_vars.apply(this, arguments); 309 | this.uses_arguments = false; 310 | 311 | var symbol = new AST_VarDef({ name: "arguments", start: this.start, end: this.end }); 312 | var def = new SymbolDef(this, this.variables.size(), symbol); 313 | this.variables.set(symbol.name, def); 314 | }); 315 | 316 | AST_SymbolRef.DEFMETHOD("reference", function() { 317 | var def = this.definition(); 318 | def.references.push(this); 319 | var s = this.scope; 320 | while (s) { 321 | push_uniq(s.enclosed, def); 322 | if (s === def.scope) break; 323 | s = s.parent_scope; 324 | } 325 | this.frame = this.scope.nesting - def.scope.nesting; 326 | }); 327 | 328 | AST_Scope.DEFMETHOD("find_variable", function(name){ 329 | if (name instanceof AST_Symbol) name = name.name; 330 | return this.variables.get(name) 331 | || (this.parent_scope && this.parent_scope.find_variable(name)); 332 | }); 333 | 334 | AST_Scope.DEFMETHOD("def_function", function(symbol, in_export){ 335 | this.functions.set(symbol.name, this.def_variable(symbol, in_export)); 336 | }); 337 | 338 | AST_Scope.DEFMETHOD("def_variable", function(symbol, in_export){ 339 | var def; 340 | if (!this.variables.has(symbol.name)) { 341 | def = new SymbolDef(this, this.variables.size(), symbol); 342 | this.variables.set(symbol.name, def); 343 | def.object_destructuring_arg = symbol.object_destructuring_arg; 344 | if (in_export) { 345 | def.export = true; 346 | } 347 | def.global = !this.parent_scope && !(symbol instanceof AST_SymbolBlockDeclaration); 348 | } else { 349 | def = this.variables.get(symbol.name); 350 | def.orig.push(symbol); 351 | } 352 | return symbol.thedef = def; 353 | }); 354 | 355 | AST_Scope.DEFMETHOD("next_mangled", function(options){ 356 | var ext = this.enclosed; 357 | out: while (true) { 358 | var m = base54(++this.cname); 359 | if (!is_identifier(m)) continue; // skip over "do" 360 | 361 | // https://github.com/mishoo/UglifyJS2/issues/242 -- do not 362 | // shadow a name excepted from mangling. 363 | if (options.except.indexOf(m) >= 0) continue; 364 | 365 | // we must ensure that the mangled name does not shadow a name 366 | // from some parent scope that is referenced in this or in 367 | // inner scopes. 368 | for (var i = ext.length; --i >= 0;) { 369 | var sym = ext[i]; 370 | var name = sym.mangled_name || (sym.unmangleable(options) && sym.name); 371 | if (m == name) continue out; 372 | } 373 | return m; 374 | } 375 | }); 376 | 377 | AST_Function.DEFMETHOD("next_mangled", function(options, def){ 378 | // #179, #326 379 | // in Safari strict mode, something like (function x(x){...}) is a syntax error; 380 | // a function expression's argument cannot shadow the function expression's name 381 | 382 | var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition(); 383 | while (true) { 384 | var name = AST_Lambda.prototype.next_mangled.call(this, options, def); 385 | if (!(tricky_def && tricky_def.mangled_name == name)) 386 | return name; 387 | } 388 | }); 389 | 390 | AST_Scope.DEFMETHOD("references", function(sym){ 391 | if (sym instanceof AST_Symbol) sym = sym.definition(); 392 | return this.enclosed.indexOf(sym) < 0 ? null : sym; 393 | }); 394 | 395 | AST_Symbol.DEFMETHOD("unmangleable", function(options){ 396 | var def = this.definition(); 397 | return def && def.unmangleable(options); 398 | }); 399 | 400 | // property accessors are not mangleable 401 | AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){ 402 | return true; 403 | }); 404 | 405 | // labels are always mangleable 406 | AST_Label.DEFMETHOD("unmangleable", function(){ 407 | return false; 408 | }); 409 | 410 | AST_Symbol.DEFMETHOD("unreferenced", function(){ 411 | return this.definition().references.length == 0 412 | && !(this.scope.uses_eval || this.scope.uses_with); 413 | }); 414 | 415 | AST_Symbol.DEFMETHOD("undeclared", function(){ 416 | return this.definition().undeclared; 417 | }); 418 | 419 | AST_LabelRef.DEFMETHOD("undeclared", function(){ 420 | return false; 421 | }); 422 | 423 | AST_Label.DEFMETHOD("undeclared", function(){ 424 | return false; 425 | }); 426 | 427 | AST_Symbol.DEFMETHOD("definition", function(){ 428 | return this.thedef; 429 | }); 430 | 431 | AST_Symbol.DEFMETHOD("global", function(){ 432 | return this.definition().global; 433 | }); 434 | 435 | AST_Var.DEFMETHOD("has_const_pragma", function() { 436 | var comments_before = this.start && this.start.comments_before; 437 | var lastComment = comments_before && comments_before[comments_before.length - 1]; 438 | return lastComment && /@const\b/.test(lastComment.value); 439 | }); 440 | 441 | AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ 442 | return defaults(options, { 443 | except : [], 444 | eval : false, 445 | sort : false, // Ignored. Flag retained for backwards compatibility. 446 | toplevel : false, 447 | screw_ie8 : false, 448 | keep_fnames : false, 449 | keep_classnames : false 450 | }); 451 | }); 452 | 453 | AST_Toplevel.DEFMETHOD("mangle_names", function(options){ 454 | options = this._default_mangler_options(options); 455 | 456 | // Never mangle arguments 457 | options.except.push('arguments'); 458 | 459 | // We only need to mangle declaration nodes. Special logic wired 460 | // into the code generator will display the mangled name if it's 461 | // present (and for AST_SymbolRef-s it'll use the mangled name of 462 | // the AST_SymbolDeclaration that it points to). 463 | var lname = -1; 464 | var to_mangle = []; 465 | 466 | if (options.cache) { 467 | this.globals.each(function(symbol){ 468 | if (options.except.indexOf(symbol.name) < 0) { 469 | to_mangle.push(symbol); 470 | } 471 | }); 472 | } 473 | 474 | var tw = new TreeWalker(function(node, descend){ 475 | if (node instanceof AST_LabeledStatement) { 476 | // lname is incremented when we get to the AST_Label 477 | var save_nesting = lname; 478 | descend(); 479 | lname = save_nesting; 480 | return true; // don't descend again in TreeWalker 481 | } 482 | if (node instanceof AST_Scope) { 483 | var p = tw.parent(), a = []; 484 | node.variables.each(function(symbol){ 485 | if (options.except.indexOf(symbol.name) < 0) { 486 | a.push(symbol); 487 | } 488 | }); 489 | to_mangle.push.apply(to_mangle, a); 490 | return; 491 | } 492 | if (node instanceof AST_Label) { 493 | var name; 494 | do name = base54(++lname); while (!is_identifier(name)); 495 | node.mangled_name = name; 496 | return true; 497 | } 498 | var mangle_with_block_scope = 499 | (options.screw_ie8 && node instanceof AST_SymbolCatch) || 500 | node instanceof AST_SymbolBlockDeclaration; 501 | if (mangle_with_block_scope) { 502 | to_mangle.push(node.definition()); 503 | return; 504 | } 505 | }); 506 | this.walk(tw); 507 | to_mangle.forEach(function(def){ 508 | if (def.destructuring && !def.destructuring.is_array) return; 509 | def.mangle(options); 510 | }); 511 | 512 | if (options.cache) { 513 | options.cache.cname = this.cname; 514 | } 515 | }); 516 | 517 | AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ 518 | options = this._default_mangler_options(options); 519 | var tw = new TreeWalker(function(node){ 520 | if (node instanceof AST_Constant) 521 | base54.consider(node.print_to_string()); 522 | else if (node instanceof AST_Return) 523 | base54.consider("return"); 524 | else if (node instanceof AST_Throw) 525 | base54.consider("throw"); 526 | else if (node instanceof AST_Continue) 527 | base54.consider("continue"); 528 | else if (node instanceof AST_Break) 529 | base54.consider("break"); 530 | else if (node instanceof AST_Debugger) 531 | base54.consider("debugger"); 532 | else if (node instanceof AST_Directive) 533 | base54.consider(node.value); 534 | else if (node instanceof AST_While) 535 | base54.consider("while"); 536 | else if (node instanceof AST_Do) 537 | base54.consider("do while"); 538 | else if (node instanceof AST_If) { 539 | base54.consider("if"); 540 | if (node.alternative) base54.consider("else"); 541 | } 542 | else if (node instanceof AST_Var) 543 | base54.consider("var"); 544 | else if (node instanceof AST_Const) 545 | base54.consider("const"); 546 | else if (node instanceof AST_Lambda) 547 | base54.consider("function"); 548 | else if (node instanceof AST_For) 549 | base54.consider("for"); 550 | else if (node instanceof AST_ForIn) 551 | base54.consider("for in"); 552 | else if (node instanceof AST_Switch) 553 | base54.consider("switch"); 554 | else if (node instanceof AST_Case) 555 | base54.consider("case"); 556 | else if (node instanceof AST_Default) 557 | base54.consider("default"); 558 | else if (node instanceof AST_With) 559 | base54.consider("with"); 560 | else if (node instanceof AST_ObjectSetter) 561 | base54.consider("set" + node.key); 562 | else if (node instanceof AST_ObjectGetter) 563 | base54.consider("get" + node.key); 564 | else if (node instanceof AST_ObjectKeyVal) 565 | base54.consider(node.key); 566 | else if (node instanceof AST_New) 567 | base54.consider("new"); 568 | else if (node instanceof AST_This) 569 | base54.consider("this"); 570 | else if (node instanceof AST_Super) 571 | base54.consider("super"); 572 | else if (node instanceof AST_Try) 573 | base54.consider("try"); 574 | else if (node instanceof AST_Catch) 575 | base54.consider("catch"); 576 | else if (node instanceof AST_Finally) 577 | base54.consider("finally"); 578 | else if (node instanceof AST_Symbol && node.unmangleable(options)) 579 | base54.consider(node.name); 580 | else if (node instanceof AST_Unary || node instanceof AST_Binary) 581 | base54.consider(node.operator); 582 | else if (node instanceof AST_Dot) 583 | base54.consider(node.property); 584 | }); 585 | this.walk(tw); 586 | base54.sort(); 587 | }); 588 | 589 | var base54 = (function() { 590 | var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789"; 591 | var chars, frequency; 592 | function reset() { 593 | frequency = Object.create(null); 594 | chars = string.split("").map(function(ch){ return ch.charCodeAt(0) }); 595 | chars.forEach(function(ch){ frequency[ch] = 0 }); 596 | } 597 | base54.consider = function(str){ 598 | for (var i = str.length; --i >= 0;) { 599 | var code = str.charCodeAt(i); 600 | if (code in frequency) ++frequency[code]; 601 | } 602 | }; 603 | base54.sort = function() { 604 | chars = mergeSort(chars, function(a, b){ 605 | if (is_digit(a) && !is_digit(b)) return 1; 606 | if (is_digit(b) && !is_digit(a)) return -1; 607 | return frequency[b] - frequency[a]; 608 | }); 609 | }; 610 | base54.reset = reset; 611 | reset(); 612 | base54.get = function(){ return chars }; 613 | base54.freq = function(){ return frequency }; 614 | function base54(num) { 615 | var ret = "", base = 54; 616 | num++; 617 | do { 618 | num--; 619 | ret += String.fromCharCode(chars[num % base]); 620 | num = Math.floor(num / base); 621 | base = 64; 622 | } while (num > 0); 623 | return ret; 624 | }; 625 | return base54; 626 | })(); 627 | 628 | AST_Toplevel.DEFMETHOD("scope_warnings", function(options){ 629 | options = defaults(options, { 630 | undeclared : false, // this makes a lot of noise 631 | unreferenced : true, 632 | assign_to_global : true, 633 | func_arguments : true, 634 | nested_defuns : true, 635 | eval : true 636 | }); 637 | var tw = new TreeWalker(function(node){ 638 | if (options.undeclared 639 | && node instanceof AST_SymbolRef 640 | && node.undeclared()) 641 | { 642 | // XXX: this also warns about JS standard names, 643 | // i.e. Object, Array, parseInt etc. Should add a list of 644 | // exceptions. 645 | AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", { 646 | name: node.name, 647 | file: node.start.file, 648 | line: node.start.line, 649 | col: node.start.col 650 | }); 651 | } 652 | if (options.assign_to_global) 653 | { 654 | var sym = null; 655 | if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) 656 | sym = node.left; 657 | else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef) 658 | sym = node.init; 659 | if (sym 660 | && (sym.undeclared() 661 | || (sym.global() && sym.scope !== sym.definition().scope))) { 662 | AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", { 663 | msg: sym.undeclared() ? "Accidental global?" : "Assignment to global", 664 | name: sym.name, 665 | file: sym.start.file, 666 | line: sym.start.line, 667 | col: sym.start.col 668 | }); 669 | } 670 | } 671 | if (options.eval 672 | && node instanceof AST_SymbolRef 673 | && node.undeclared() 674 | && node.name == "eval") { 675 | AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start); 676 | } 677 | if (options.unreferenced 678 | && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label) 679 | && !(node instanceof AST_SymbolCatch) 680 | && node.unreferenced()) { 681 | AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", { 682 | type: node instanceof AST_Label ? "Label" : "Symbol", 683 | name: node.name, 684 | file: node.start.file, 685 | line: node.start.line, 686 | col: node.start.col 687 | }); 688 | } 689 | if (options.func_arguments 690 | && node instanceof AST_Lambda 691 | && node.uses_arguments) { 692 | AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", { 693 | name: node.name ? node.name.name : "anonymous", 694 | file: node.start.file, 695 | line: node.start.line, 696 | col: node.start.col 697 | }); 698 | } 699 | if (options.nested_defuns 700 | && node instanceof AST_Defun 701 | && !(tw.parent() instanceof AST_Scope)) { 702 | AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", { 703 | name: node.name.name, 704 | type: tw.parent().TYPE, 705 | file: node.start.file, 706 | line: node.start.line, 707 | col: node.start.col 708 | }); 709 | } 710 | }); 711 | this.walk(tw); 712 | }); 713 | -------------------------------------------------------------------------------- /lib/exe.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013 Craig Condon 3 | * Copyright (c) 2015-2016 Jared Allard 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to 11 | * the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | * 24 | **/ 25 | 26 | 'use strict'; 27 | 28 | const async = require("async"), 29 | mkdirp = require("mkdirp"), 30 | recursiveReadSync = require('recursive-readdir-sync'), 31 | request = require("request"), 32 | gunzip = require("gunzip-maybe"), 33 | path = require("path"), 34 | fs = require("fs-extra"), 35 | tarstream = require('tar-stream'), 36 | colors = require('colors'), 37 | ncp = require("ncp").ncp, 38 | ProgressBar = require("progress"), 39 | child_process = require("child_process"), 40 | glob = require("glob"), 41 | bundle = require("./bundle"), 42 | embed = require("./embed"), 43 | os = require("os"), 44 | _log = require("./log"), 45 | _monkeypatch = require("./monkeypatch"), 46 | spawn = child_process.spawn; 47 | 48 | const isWin = /^win/.test(process.platform); 49 | 50 | let isPy, 51 | framework, 52 | version; 53 | 54 | /** 55 | * Compiliation process. 56 | */ 57 | 58 | exports.compile = function(options, complete) { 59 | 60 | var nodeCompiler, nexeEntryPath; 61 | 62 | async.waterfall([ 63 | /** 64 | *check relevant options 65 | */ 66 | function checkOpts(next) { 67 | /* failsafe */ 68 | if (options === undefined) { 69 | _log("error", "no options given to .compile()"); 70 | process.exit() 71 | } 72 | 73 | /** 74 | * Have we been given a custom flag for python executable? 75 | **/ 76 | if (options.python !== 'python' && options.python !== "" && options.python !== undefined) { 77 | if (isWin) { 78 | isPy = options.python.replace(/\//gm, "\\"); // use windows file paths, batch is sensitive. 79 | } else { 80 | isPy = options.python; 81 | } 82 | 83 | _log("set python as " + isPy); 84 | } else { 85 | isPy = "python"; 86 | } 87 | 88 | // remove dots 89 | options.framework = options.framework.replace(/\./g, ""); 90 | 91 | // set outter-scope framework variable. 92 | framework = options.framework; 93 | _log("framework => " + framework); 94 | 95 | version = options.nodeVersion; // better framework vc 96 | 97 | // check iojs version 98 | if (framework === "iojs" && version === "latest") { 99 | _log("fetching iojs versions"); 100 | mkdirp(options.nodeTempDir); // make temp dir, probably repetive. 101 | 102 | // create write stream so we have control over events 103 | var output = fs.createWriteStream(path.join(options.nodeTempDir, 104 | "iojs-versions.json")); 105 | 106 | request.get("https://iojs.org/dist/index.json") 107 | .pipe(output); 108 | 109 | output.on('close', function() { 110 | _log("done"); 111 | var f = fs.readFileSync(path.join(options.nodeTempDir, 112 | "iojs-versions.json")); 113 | f = JSON.parse(f); 114 | version = f[0].version.replace("v", ""); 115 | 116 | _log("iojs latest => " + version); 117 | 118 | // continue down along the async road 119 | next(); 120 | }); 121 | } else { 122 | next(); 123 | } 124 | }, 125 | 126 | /** 127 | * first download node 128 | */ 129 | function downloadNode(next) { 130 | _downloadNode(version, options.nodeTempDir, options.nodeConfigureArgs, options.nodeMakeArgs, options.nodeVCBuildArgs, next); 131 | }, 132 | 133 | /** 134 | * Embed Resources into a base64 encoded array. 135 | **/ 136 | function embedResources(nc, next) { 137 | nodeCompiler = nc; 138 | 139 | _log("embedResources %s", options.resourceFiles); 140 | embed(options.resourceFiles, options.resourceRoot, nc, next); 141 | }, 142 | 143 | /** 144 | * Write compiledres.js 145 | **/ 146 | function writeResources(resources, next) { 147 | let resourcePath = path.join(nodeCompiler.dir, "lib", "compiledres.js"); 148 | _log("resource -> %s", resourcePath); 149 | 150 | fs.writeFile(resourcePath, resources, next); 151 | }, 152 | 153 | /** 154 | * Bundle the application into one script 155 | **/ 156 | function combineProject(next) { 157 | _log("bundle %s", options.input); 158 | bundle(options.input, nodeCompiler.dir, options, next); 159 | }, 160 | 161 | 162 | /** 163 | * monkeypatch some files so that the compiled.js file is loaded when the app runs 164 | */ 165 | 166 | function monkeyPatchNodeConfig(next) { 167 | _monkeyPatchNodeConfig(nodeCompiler, next); 168 | }, 169 | 170 | /** 171 | * monkeypatch node.cc to prevent v8 and node from processing CLI flags 172 | */ 173 | function monkeyPatchNodeCc(next) { 174 | if (options.flags) { 175 | _monkeyPatchMainCc(nodeCompiler, next); 176 | } else { 177 | next(); 178 | } 179 | }, 180 | 181 | function monkeyPatchv8FlagsCc(next) { 182 | if (options.jsFlags) { 183 | return _monkeyPatchv8FlagsCc(nodeCompiler, options, next); 184 | } 185 | 186 | return next(); 187 | }, 188 | 189 | /** 190 | * monkeypatch child_process.js so nexejs knows when it is a forked process 191 | */ 192 | function monkeyPatchChildProc(next) { 193 | _monkeyPatchChildProcess(nodeCompiler, next); 194 | }, 195 | 196 | //process native modules 197 | function embedNativeModules(next) { 198 | var browserify = require('browserify'), 199 | UglifyJS = require("../tools/node"); 200 | 201 | var libraries=[]; 202 | var includeDirs=[]; 203 | 204 | async.map(options.native,function(nativeModule,cb) 205 | { 206 | async.waterfall([ 207 | /** 208 | * generate single js file 209 | * modify js change require .node => process.binding 210 | */ 211 | function processSingleJS(n){ 212 | 213 | const bundlePath = path.join(nodeCompiler.dir, "lib", nativeModule.name+".js"); 214 | const ws = fs.createWriteStream(bundlePath); 215 | const paths = [path.join(nodeCompiler.dir, 'lib')]; 216 | const bproc = browserify([nativeModule.js], { 217 | commondir: false, 218 | paths: paths, 219 | builtins: false, 220 | insertGlobalVars: false, 221 | detectGlobals: true, 222 | browserField: false 223 | }); 224 | 225 | const bprocbun = bproc 226 | .bundle() // bundle 227 | .pipe(ws) // pipe to file 228 | 229 | ws.on('close', function() { 230 | var source = fs.readFileSync(bundlePath, 'utf8'); 231 | source = source.replace(/[^\x00-\x7F]/g, ""); 232 | 233 | // modify js change require .node => process.binding 234 | source = source.replace(/require\(.*?\.node.*?\)/g, "process\.binding(\'"+nativeModule.name+"\')"); 235 | 236 | var result = UglifyJS.minify(source, {fromString: true}); 237 | source = result.code; 238 | // write the source modified to compiled.js 239 | fs.writeFileSync(bundlePath, source, {encoding:'utf8'}); 240 | n(); 241 | }); 242 | }, 243 | /** 244 | * copy cc files 245 | * patch main cc files 246 | */ 247 | function processCCFile(n) 248 | { 249 | var modulePath = path.join(nodeCompiler.dir, "src", nativeModule.name); 250 | fs.ensureDirSync(modulePath); 251 | 252 | //copy source files 253 | if(nativeModule.hasOwnProperty('sources')){ 254 | if(Array.isArray(nativeModule.sources)){ 255 | for(var i=0;i0){ 353 | var libs = ""; 354 | for(var i in libraries){ 355 | libs += "'"+libraries[i]+"'," 356 | } 357 | libs = libs.substr(0,libs.length-1) 358 | var libraries_entity = "'libraries':["+libs+"]," 359 | if(source.indexOf(libraries_entity)==-1){ 360 | source = source.replace("'defines':",libraries_entity+"\n'defines':"); 361 | } 362 | } 363 | 364 | // process include_dirs entity 365 | if(includeDirs.length>0){ 366 | var include_dirs = ""; 367 | for(var i in includeDirs){ 368 | include_dirs += "'"+includeDirs[i]+"'," 369 | } 370 | 371 | var include_dirs_entity = "'src',"+include_dirs; 372 | if(source.indexOf(include_dirs_entity)==-1){ 373 | source = source.replace("'src',",include_dirs_entity); 374 | } 375 | } 376 | 377 | fs.writeFileSync(extFile, source, "utf8"); 378 | next(); 379 | }); 380 | 381 | }, 382 | 383 | 384 | /** 385 | * If an old compiled executable exists in the Release directory, delete it. 386 | * This lets us see if the build failed by checking the existence of this file later. 387 | */ 388 | 389 | function cleanUpOldExecutable(next) { 390 | fs.unlink(nodeCompiler.releasePath, function(err) { 391 | if (err) { 392 | if (err.code === "ENOENT") { 393 | next(); 394 | } else { 395 | throw err; 396 | } 397 | } else { 398 | next(); 399 | } 400 | }); 401 | }, 402 | 403 | /** 404 | * compile the node application 405 | */ 406 | 407 | function makeExecutable(next) { 408 | if (isWin) { 409 | _log("vcbuild [make stage]"); 410 | } else { 411 | _log("make"); 412 | } 413 | nodeCompiler.make(next); 414 | }, 415 | 416 | /** 417 | * we create the output directory if needed 418 | */ 419 | 420 | function makeOutputDirectory(next) { 421 | mkdirp(path.dirname(options.output), function() { 422 | next(); 423 | }); 424 | }, 425 | 426 | /** 427 | * Verify that the executable was compiled successfully 428 | */ 429 | 430 | function checkThatExecutableExists(next) { 431 | fs.exists(nodeCompiler.releasePath, function(exists) { 432 | if (!exists) { 433 | _log("error", 434 | "The release executable has not been generated. " + 435 | "This indicates a failure in the build process. " + 436 | "There is likely additional information above." 437 | ); 438 | process.exit(1); 439 | } else { 440 | next(); 441 | } 442 | }); 443 | }, 444 | 445 | function stripBinary(next) 446 | { 447 | var cmd = "/usr/bin/strip"; 448 | if(options.hasOwnProperty("stripBinary") && 449 | options.stripBinary==true && 450 | fs.existsSync(cmd)) 451 | { 452 | _log("strip binary %s", nodeCompiler.releasePath); 453 | child_process.execFileSync(cmd, [nodeCompiler.releasePath], []); 454 | _log('striped'); 455 | } 456 | next(); 457 | }, 458 | 459 | /** 460 | * Copy the compilied binary to the output specified. 461 | */ 462 | 463 | function copyBinaryToOutput(next) { 464 | _log("cp %s %s", nodeCompiler.releasePath, options.output); 465 | ncp(nodeCompiler.releasePath, options.output, function(err) { 466 | if (err) { 467 | _log("error", "Couldn't copy binary."); 468 | throw err; // dump raw error object 469 | } 470 | _log('copied'); 471 | 472 | next(); 473 | }); 474 | } 475 | 476 | 477 | 478 | ], complete); 479 | } 480 | 481 | /** 482 | * Download a version of node 483 | * 484 | * @param {string} version, version of node to download 485 | * @param {string} directory, where to store the downloaded src 486 | * @param {function} complete, callback 487 | */ 488 | 489 | function _downloadNode(version, directory, nodeConfigureArgs, nodeMakeArgs, nodeVCBuildArgs, complete) { 490 | var nodeFileDir = path.resolve(path.join(directory, framework, version)), // fixes #107, was process.cwd(), + rest. 491 | nodeFilePath = path.resolve(path.join(nodeFileDir, framework + "-" + version + ".tar.gz")); 492 | 493 | 494 | // might already be downloaded, and unzipped 495 | if (_getNodeCompiler(nodeFileDir, nodeConfigureArgs, nodeMakeArgs, nodeVCBuildArgs, complete)) { 496 | return; 497 | } 498 | 499 | 500 | async.waterfall([ 501 | 502 | /** 503 | * first make the directory where the zip file will live 504 | */ 505 | 506 | function makeDirectory(next) { 507 | mkdirp.sync(path.dirname(nodeFilePath)); 508 | next(); 509 | }, 510 | 511 | /** 512 | * download node into the target directory 513 | */ 514 | 515 | function downloadNode(next) { 516 | if (fs.existsSync(nodeFilePath)) return next(); 517 | 518 | var uri = framework; 519 | 520 | if (framework === "node") { 521 | uri = 'nodejs'; // if node, use nodejs uri 522 | } else if (framework === 'nodejs') { 523 | framework = 'node'; // support nodejs, and node, as framework. 524 | } 525 | 526 | var type = global.type; 527 | var url, prefix = "https://" + uri + ".org/dist"; 528 | 529 | if (version === "latest") { 530 | url = prefix + "/" + framework + "-" + version + ".tar.gz"; 531 | } else { 532 | url = prefix + "/v" + version + "/" + framework + "-v" + version + ".tar.gz"; 533 | } 534 | 535 | _log("downloading %s", url); 536 | 537 | var output = fs.createWriteStream(nodeFilePath, { 538 | "flags": "w+" 539 | }); 540 | 541 | // need to set user-agent to bypass some corporate firewalls 542 | var requestOptions = { 543 | url: url, 544 | headers: { 545 | "User-Agent": "Node.js" 546 | } 547 | } 548 | 549 | _logProgress(request(requestOptions)).pipe(output); 550 | 551 | output.on("close", function() { 552 | next(); 553 | }); 554 | }, 555 | 556 | /** 557 | * unzip in the same directory 558 | */ 559 | 560 | function unzipNodeTarball(next) { 561 | var onError = function(err) { 562 | console.log(err.stack); 563 | _log("error", "failed to extract the node source"); 564 | process.exit(1); 565 | } 566 | 567 | if (isWin) { 568 | _log("extracting the node source [node-tar.gz]"); 569 | 570 | // tar-stream method w/ gunzip-maybe 571 | var read = fs.createReadStream(nodeFilePath); 572 | var extract = tarstream.extract() 573 | var basedir = nodeFileDir; 574 | 575 | if (!fs.existsSync(nodeFileDir)) { 576 | fs.mkdirSync(nodeFileDir); 577 | } 578 | 579 | extract.on('entry', function(header, stream, callback) { 580 | // header is the tar header 581 | // stream is the content body (might be an empty stream) 582 | // call next when you are done with this entry 583 | 584 | var absolutepath = path.join(basedir, header.name); 585 | if (header.type === 'directory') { 586 | // handle directories. 587 | // console.log('dir:', header.name); 588 | fs.mkdirSync(absolutepath); 589 | return callback(); 590 | } else if (header.type === 'file') { 591 | // handle files 592 | // console.log('file:', header.name); 593 | } else { 594 | console.log(header.type + ':', header.name); 595 | _log('warn', 'unhandled type in tar extraction, skipping'); 596 | return callback(); 597 | } 598 | 599 | var write = fs.createWriteStream(absolutepath); 600 | 601 | stream.pipe(write); 602 | 603 | write.on('close', function() { 604 | return callback(); 605 | }); 606 | 607 | stream.on('error', function(err) { 608 | return onError(err); 609 | }) 610 | 611 | write.on('error', function(err) { 612 | return onError(err); 613 | }); 614 | 615 | stream.resume() // just auto drain the stream 616 | }) 617 | 618 | extract.on('finish', function() { 619 | _log('extraction finished'); 620 | return next(); 621 | }) 622 | 623 | read.pipe(gunzip()).pipe(extract); 624 | } else { 625 | _log("extracting the node source [native tar]"); 626 | 627 | var cmd = ["tar", "-xf", nodeFilePath, "-C", nodeFileDir]; 628 | _log(cmd.join(" ")); 629 | 630 | var tar = spawn(cmd.shift(), cmd); 631 | tar.stdout.pipe(process.stdout); 632 | tar.stderr.pipe(process.stderr); 633 | 634 | tar.on("close", function() { 635 | return next(); 636 | }); 637 | tar.on("error", onError); 638 | } 639 | }, 640 | 641 | /** 642 | * return the compiler object for the node version 643 | */ 644 | 645 | function(next, type) { 646 | _getNodeCompiler(nodeFileDir, nodeConfigureArgs, nodeMakeArgs, nodeVCBuildArgs, next, type) 647 | }, 648 | 649 | ], complete); 650 | } 651 | 652 | /** 653 | * Get the compilier we will use for whatever platform we may be on and configure 654 | * it. 655 | */ 656 | 657 | function _getNodeCompiler(nodeFileDir, nodeConfigureArgs, nodeMakeArgs, nodeVCBuildArgs, complete, type) { 658 | var dir = _getFirstDirectory(nodeFileDir); 659 | 660 | // standard 661 | var executable = "node.exe"; 662 | var binary = "node"; 663 | 664 | // iojs specifics. 665 | if (framework === "iojs") { 666 | executable = "iojs.exe"; 667 | binary = "iojs"; 668 | } 669 | 670 | if (dir) { 671 | if (isWin) { 672 | complete(null, { 673 | dir: dir, 674 | version: path.basename(nodeFileDir), 675 | releasePath: path.join(dir, "Release", executable), 676 | make: function(next) { 677 | // create a new env with minimal impact on old one 678 | var newEnv = process.env 679 | 680 | if (isPy !== "python") { 681 | // add the dir of the suposed python exe to path 682 | newEnv.path = process.env.PATH + ";" + path.dirname(isPy) 683 | } 684 | 685 | if (!nodeVCBuildArgs || !nodeVCBuildArgs.length) { 686 | nodeVCBuildArgs = [ "nosign" ]; // "release" is already the default config value in VCBuild.bat 687 | } 688 | 689 | // spawn a vcbuild process with our custom enviroment. 690 | var vcbuild = spawn("vcbuild.bat", nodeVCBuildArgs, { 691 | cwd: dir, 692 | env: newEnv 693 | }); 694 | vcbuild.stdout.pipe(process.stdout); 695 | vcbuild.stderr.pipe(process.stderr); 696 | vcbuild.on("close", function() { 697 | next(); 698 | }); 699 | } 700 | }); 701 | } else { 702 | complete(null, { 703 | dir: dir, 704 | version: path.basename(nodeFileDir), 705 | releasePath: path.join(dir, "out", "Release", binary), 706 | make: function(next) { 707 | var cfg = "./configure", 708 | configure; 709 | 710 | var conf = [cfg]; 711 | 712 | if (isPy !== "python") { 713 | conf = [conf].concat(nodeConfigureArgs); 714 | } 715 | 716 | // should work for all use cases now. 717 | configure = spawn(isPy, conf, { 718 | cwd: dir.toString('ascii') 719 | }); 720 | 721 | // local function, move to top eventually 722 | function _loop(dir) { 723 | /* eventually try every python file */ 724 | var pdir = fs.readdirSync(dir); 725 | 726 | pdir.forEach(function(v, i) { 727 | var stat = fs.statSync(dir + "/" + v); 728 | if (stat.isFile()) { 729 | // only process Makefiles and .mk targets. 730 | if (v !== "Makefile" && path.extname(v) !== ".mk") { 731 | return; 732 | } 733 | 734 | _log("patching " + v); 735 | 736 | /* patch the file */ 737 | var py = fs.readFileSync(dir + "/" + v, { 738 | encoding: 'utf8' 739 | }); 740 | py = py.replace(/([a-z]|\/)*python(\w|)/gm, isPy); // this is definently needed 741 | fs.writeFileSync(dir + "/" + v, py, { 742 | encoding: 'utf8' 743 | }); // write to file 744 | 745 | } else if (stat.isDirectory()) { 746 | // must be dir? 747 | // skip tests because we don't need them here 748 | if (v !== "test") { 749 | _loop(dir + "/" + v) 750 | } 751 | } 752 | }); 753 | } 754 | 755 | configure.stdout.pipe(process.stdout); 756 | configure.stderr.pipe(process.stderr); 757 | 758 | // on error 759 | configure.on("error", function(err) { 760 | console.log('Error:', err); 761 | console.log(''); 762 | console.log('Details:'); 763 | console.log('command =', isPy, cfg, conf_args || ''); 764 | console.log('cwd =', dir); 765 | 766 | var configure_path = path.join(dir, 'configure'); 767 | var contains_configure = fs.existsSync(configure_path); 768 | 769 | console.log('cwd contains configure,', (contains_configure ? colors.green('yes') : colors.red('no'))); 770 | 771 | var configure_size = fs.statSync(configure_path).size; 772 | 773 | console.log('configure is non-zero size,', ((configure_size > 0) ? colors.green('yes') : colors.red('no'))); 774 | 775 | _log("error", "failed to launch configure."); 776 | process.exit(1); 777 | }); 778 | 779 | // when it's finished 780 | configure.on("close", function() { 781 | if (isPy !== "python") { 782 | /** 783 | * Originally I thought this only applied to io.js, 784 | * however I soon found out this affects node.js, 785 | * so it is now mainstream. 786 | */ 787 | _log("preparing python"); 788 | 789 | // loop over depends 790 | _loop(dir); 791 | } 792 | 793 | if (nodeMakeArgs === undefined) { 794 | nodeMakeArgs = []; 795 | } 796 | 797 | var platformMake = "make"; 798 | if (os.platform().match(/bsd$/) != null) { 799 | platformMake = "gmake"; 800 | } 801 | 802 | var make = spawn(platformMake, nodeMakeArgs, { 803 | cwd: dir 804 | }); 805 | make.stdout.pipe(process.stdout); 806 | make.stderr.pipe(process.stderr); 807 | make.on("error", function(err) { 808 | console.log(err); 809 | _log("error", "failed to run make."); 810 | process.exit(1); 811 | }) 812 | make.on("close", function() { 813 | next(); 814 | }); 815 | }) 816 | } 817 | }); 818 | } 819 | return true; 820 | } 821 | 822 | return false; 823 | } 824 | 825 | /** 826 | */ 827 | 828 | function _monkeyPatchNodeConfig(compiler, complete) { 829 | async.waterfall([ 830 | /** 831 | * monkeypatch the gyp file to include the compiled.js and compiledres.js files 832 | */ 833 | function(next) { 834 | _monkeyPatchGyp(compiler, next) 835 | }, 836 | 837 | /** 838 | * monkeypatch main entry point 839 | */ 840 | function(next) { 841 | _monkeyPatchMainJs(compiler, next) 842 | } 843 | ], complete); 844 | } 845 | 846 | /** 847 | * patch the gyp file to allow our custom includes 848 | */ 849 | 850 | function _monkeyPatchGyp(compiler, complete) { 851 | 852 | var gypPath = path.join(compiler.dir, "node.gyp"); 853 | 854 | _monkeypatch( 855 | gypPath, 856 | function(content) { 857 | return ~content.indexOf("compiled.js"); 858 | }, 859 | function(content, next) { 860 | next(null, content.replace("'lib/fs.js',", "'lib/fs.js', 'lib/compiled.js', 'lib/compiledres.js', ")) 861 | }, 862 | complete 863 | ) 864 | } 865 | 866 | /** 867 | */ 868 | 869 | function _monkeyPatchMainJs(compiler, complete) { 870 | var mainPath = path.join(compiler.dir, "src", "node.js"); 871 | 872 | if(!fs.existsSync(mainPath)) { 873 | //_log('warn', 'src/node.js doesn\'t exist. Trying \'lib/internal/bootstrap_node.js\'') 874 | mainPath = path.join(compiler.dir, "lib/internal", "bootstrap_node.js"); 875 | } 876 | 877 | _monkeypatch( 878 | mainPath, 879 | function(content) { 880 | return ~content.indexOf("node-compiler"); 881 | }, 882 | function(content, next) { 883 | next(null, content.replace(/\(function\(process\) \{/, '\ 884 | (function(process) {\n\ 885 | //node-compiler injected \n\ 886 | process._eval = \'require("compiled");\';\n\ 887 | if (process.argv[1] !== "compiled.js") {\n\ 888 | process.argv.splice(1, 0, "compiled.js");\n\ 889 | }\n\ 890 | ')) 891 | }, 892 | complete 893 | ); 894 | } 895 | 896 | /** 897 | * Make child_process work. 898 | */ 899 | function _monkeyPatchChildProcess(compiler, complete) { 900 | var childProcPath = path.join(compiler.dir, "lib", "child_process.js"); 901 | 902 | _monkeypatch( 903 | childProcPath, 904 | function(content) { 905 | return ~content.indexOf("--child_process"); 906 | }, 907 | function(content, next) { 908 | next(null, content.replace(/return spawn\(/, 'args.unshift("compiled.js", "--child_process");\n return spawn(')); 909 | }, 910 | complete 911 | ); 912 | } 913 | 914 | /** 915 | * Patch node.cc to not check the internal arguments. 916 | */ 917 | 918 | function _monkeyPatchMainCc(compiler, complete) { 919 | let finalContents; 920 | 921 | let mainPath = path.join(compiler.dir, "src", "node.cc"); 922 | let mainC = fs.readFileSync(mainPath, { 923 | encoding: 'utf8' 924 | }); 925 | 926 | // content split, and original start/end 927 | let constant_loc = 1; 928 | let lines = mainC.split('\n'); 929 | let startLine = lines.indexOf(' // TODO use parse opts'); 930 | let endLine = lines.indexOf(' option_end_index = i;'); // pre node 0.11.6 compat 931 | let isPatched = lines.indexOf('// NEXE_PATCH_IGNOREFLAGS'); 932 | 933 | if (isPatched !== -1) { 934 | _log('already patched node.cc'); 935 | return complete(); 936 | } 937 | 938 | /** 939 | * This is the new method of passing the args. Tested on node.js 0.12.5 940 | * and iojs 2.3.1 941 | **/ 942 | if (endLine === -1 && startLine === -1) { // only if the pre-0.12.5 failed. 943 | _log("using the after 0.12.5 method of ignoring flags."); 944 | 945 | startLine = lines.indexOf(" while (index < nargs && argv[index][0] == '-') {"); // beginning of the function 946 | endLine = lines.indexOf(' // Copy remaining arguments.'); 947 | endLine--; // space, then it's at the } 948 | 949 | constant_loc = lines.length + 1; 950 | } else { 951 | _log('using 0.10.x > method of ignoring flags'); 952 | lines[endLine] = ' option_end_index = 1;'; 953 | } 954 | 955 | /** 956 | * This is the method for 5.5.0 957 | **/ 958 | if (endLine === -1 || startLine === -1) { 959 | _log("using the after 5.5.0 method of ignoring flags."); 960 | 961 | startLine = lines.indexOf(" while (index < nargs && argv[index][0] == '-' && !short_circuit) {"); // beginning of the function 962 | endLine = lines.indexOf(' // Copy remaining arguments.'); 963 | endLine--; // space, then it's at the } 964 | 965 | constant_loc = lines.length + 1; 966 | } 967 | 968 | // other versions here. 969 | if (endLine === -1 || startLine === -1) { // failsafe. 970 | _log("error", "Failed to find a way to patch node.cc to ignoreFlags"); 971 | _log("startLine =", startLine, '| endLine =', endLine); 972 | process.exit(1); 973 | } 974 | 975 | // check if it's been done 976 | lines[constant_loc] = '// NEXE_PATCH_IGNOREFLAGS'; 977 | 978 | for (var i = startLine; i < endLine; i++) { 979 | lines[i] = undefined; // set the value to undefined so it's skipped by the join 980 | } 981 | 982 | _log('patched node.cc'); 983 | 984 | finalContents = lines.join('\n'); 985 | 986 | // write the file contents 987 | fs.writeFile(mainPath, finalContents, { 988 | encoding: 'utf8' 989 | }, function(err) { 990 | if (err) { 991 | _log('error', 'failed to write to', mainPath); 992 | return process.exit(1); 993 | } 994 | 995 | return complete(); 996 | }); 997 | } 998 | 999 | /** 1000 | * Patch flags.cc from deps/v8/src to use hard-coded flags. 1001 | * this function is very closely ready to accept custom injection code. 1002 | **/ 1003 | function _monkeyPatchv8FlagsCc(compiler, options, complete) { 1004 | var mainPath = path.join(compiler.dir, "deps/v8/src", "flags.cc"); 1005 | 1006 | fs.readFile(mainPath, { 1007 | encoding: 'utf8' 1008 | }, function(err, contents) { 1009 | if (err) { 1010 | return _log('error', 'failed to read', mainPath); 1011 | } 1012 | 1013 | // Super simple injection here. Perhaps make it an array at somepoint? 1014 | var injection = '\ 1015 | const char* nexevargs = "{{args}}";\n\ 1016 | int nexevargslen = strlen(nexevargs);\n\ 1017 | SetFlagsFromString(nexevargs, nexevargslen);\n\ 1018 | '; 1019 | 1020 | var injectionSplit = injection.split('\n'); 1021 | var injectionLength = injectionSplit.length; 1022 | var contentsSplit = contents.split('\n'); 1023 | var contentsLength = contentsSplit.length; 1024 | var lastInjectionLine = injectionSplit[injectionLength - 2]; 1025 | 1026 | var lineToInjectAfter = contentsSplit.indexOf(' ComputeFlagListHash();'); 1027 | var haveWeInjectedBefore = contentsSplit.indexOf(lastInjectionLine); 1028 | 1029 | var lineInjectDifference = contentsLength - lineToInjectAfter; 1030 | 1031 | // support for 0.12.x 1032 | if (lineToInjectAfter === -1) { 1033 | _log('warn', 'Using an expiramental support patch for 0.12.x'); 1034 | lineToInjectAfter = contentsSplit.indexOf('#undef FLAG_MODE_DEFINE_IMPLICATIONS'); 1035 | } 1036 | 1037 | // support for 0.10.x 1038 | if (lineToInjectAfter === -1) { 1039 | _log('warn', '0.12.x patch failed. Trying 0.10.0 patch'); 1040 | lineToInjectAfter = contentsSplit.indexOf('#define FLAG_MODE_DEFINE_IMPLICATIONS') + 1; 1041 | } 1042 | 1043 | // this is debug, comment out. 1044 | // _log('v8 injection is', injectionLength, 'newlines long'); 1045 | // _log('v8 flags source is', contentsLength, 'newlines long'); 1046 | 1047 | // console.log(finalContents) 1048 | 1049 | var finalContents, 1050 | dontCombine; 1051 | 1052 | if (lineToInjectAfter !== -1 && haveWeInjectedBefore === -1) { 1053 | _log('injecting v8/flags.cc'); 1054 | 1055 | // super debug 1056 | // _log('v8 injection determined by', lastInjectionLine); 1057 | // _log('v8 inject after line', lineToInjectAfter); 1058 | // _log('v8 inject needs to shift', lineInjectDifference, 'amount of lines by', injectionLength); 1059 | 1060 | // compute out the amount of space we'll need in this. 1061 | var startShiftLine = contentsLength - 1; // minus one to make up for 0 arg line. 1062 | var endShiftLine = lineToInjectAfter; 1063 | var injectRoom = injectionLength - 1; 1064 | 1065 | injectionSplit[0] = injectionSplit[0].replace('{{args}}', options.jsFlags); 1066 | 1067 | for (var i = startShiftLine; i !== endShiftLine; i--) { 1068 | contentsSplit[i + injectRoom] = contentsSplit[i]; 1069 | contentsSplit[i] = ''; 1070 | } 1071 | 1072 | var injectionPos = 0; 1073 | for (var i = 0; i !== injectionLength - 1; i++) { 1074 | contentsSplit[(lineToInjectAfter + 1) + injectionPos] = injectionSplit[injectionPos]; 1075 | injectionPos++; 1076 | } 1077 | } else if (lineToInjectAfter !== -1 && haveWeInjectedBefore !== -1) { 1078 | _log('re-injecting v8 args'); 1079 | 1080 | dontCombine = true; 1081 | finalContents = contentsSplit.join('\n'); 1082 | finalContents = finalContents.replace(/const char\* nexevargs = "[A-Z\-\_]*";/gi, 1083 | 'const char* nexevargs = "' + options.jsFlags + '";'); 1084 | } else { 1085 | _log('error', 'failed to find a suitable injection point for v8 args.', 1086 | 'File a bug report with the node version and log.'); 1087 | 1088 | _log('lineToInjectAfter=' + lineToInjectAfter, 'haveWeInjectedBefore=' + haveWeInjectedBefore); 1089 | return process.exit(1); 1090 | } 1091 | 1092 | if (!dontCombine) { 1093 | finalContents = contentsSplit.join('\n'); 1094 | } 1095 | 1096 | // write the file contents 1097 | fs.writeFile(mainPath, finalContents, { 1098 | encoding: 'utf8' 1099 | }, function(err) { 1100 | if (err) { 1101 | _log('error', 'failed to write to', mainPath); 1102 | return process.exit(1); 1103 | } 1104 | 1105 | return complete(); 1106 | }) 1107 | }); 1108 | } 1109 | 1110 | /** 1111 | * Get the first directory of a string. 1112 | */ 1113 | 1114 | function _getFirstDirectory(dir) { 1115 | var files = glob.sync(dir + "/*"); 1116 | 1117 | for (var i = files.length; i--;) { 1118 | var file = files[i]; 1119 | if (fs.statSync(file).isDirectory()) return file; 1120 | } 1121 | 1122 | return false; 1123 | } 1124 | 1125 | /** 1126 | * Log the progress of a request object. 1127 | */ 1128 | 1129 | function _logProgress(req) { 1130 | 1131 | req.on("response", function(resp) { 1132 | 1133 | var len = parseInt(resp.headers["content-length"], 10), 1134 | bar = new ProgressBar("[:bar]", { 1135 | complete: "=", 1136 | incomplete: " ", 1137 | total: len, 1138 | width: 100 // just use 100 1139 | }); 1140 | 1141 | req.on("data", function(chunk) { 1142 | bar.tick(chunk.length); 1143 | }); 1144 | }); 1145 | 1146 | req.on("error", function(err) { 1147 | console.log(err); 1148 | _log("error", "failed to download node sources,"); 1149 | process.exit(1); 1150 | }); 1151 | 1152 | return req; 1153 | } 1154 | 1155 | /** 1156 | * Attempt to parse the package.json for nexe information. 1157 | * 1158 | * @param {string} path - path to package.json 1159 | * @param {object} options - fallback options 1160 | * 1161 | * @todo implement options overriding package defaults. 1162 | * @todo make this much less hackily implemented.... 1163 | * 1164 | * @return {object} nexe.compile - options object 1165 | **/ 1166 | exports.package = function(path, options) { 1167 | let _package; // scope 1168 | 1169 | // check if the file exists 1170 | if (fs.existsSync(path) === false) { 1171 | _log("warn", "no package.json found."); 1172 | } else { 1173 | _package = require(path); 1174 | } 1175 | 1176 | if(!_package || !_package.compiler) { 1177 | _log('error', 'trying to use package.json variables, but not setup to do so!'); 1178 | process.exit(1); 1179 | } 1180 | 1181 | // replace ^$ w/ os specific extension on output 1182 | if (isWin) { 1183 | _package.compiler.output = _package.compiler.output.replace(/\^\$/, '.exe') // exe 1184 | } else { 1185 | _package.compiler.output = _package.compiler.output.replace(/\^\$/, '') // none 1186 | } 1187 | 1188 | // construct the object 1189 | let obj = { 1190 | input: (_package.compiler.input || options.i), 1191 | output: (_package.compiler.output || options.o), 1192 | flags: (_package.compiler.runtime.ignoreFlags || (options.f || false)), 1193 | resourceFiles: (_package.compiler.resources), 1194 | resourceRoot: (_package.compiler.resourceRoot), 1195 | nodeVersion: (_package.compiler.runtime.version || options.r), 1196 | nodeConfigureArgs: (_package.compiler.runtime.nodeConfigureArgs || []), 1197 | nodeMakeArgs: (_package.compiler.runtime.nodeMakeArgs || []), 1198 | nodeVCBuildArgs: (_package.compiler.runtime.nodeVCBuildArgs || []), 1199 | jsFlags: (_package.compiler.runtime['js-flags'] || options.j), 1200 | python: (_package.compiler.python || options.p), 1201 | debug: (_package.compiler.debug || options.d), 1202 | stripBinary: (_package.compiler.stripBinary || options.s), 1203 | uglify: (_package.compiler.uglify || options.u), 1204 | nodeTempDir: (_package.compiler.temp || options.t), 1205 | framework: (_package.compiler.runtime.framework || options.f), 1206 | native: _package.compiler.native || {} 1207 | } 1208 | 1209 | // browserify options 1210 | if(_package.compiler.browserify !== undefined) { 1211 | obj.browserifyRequires = (_package.compiler.browserify.requires || []); 1212 | obj.browserifyExcludes = (_package.compiler.browserify.excludes || []); 1213 | obj.browserifyPaths = (_package.compiler.browserify.paths || []); 1214 | } 1215 | 1216 | // TODO: get rid of this crappy code I wrote and make it less painful to read. 1217 | Object.keys(_package.compiler).forEach(function(v, i) { 1218 | if (v !== "runtime" && v !== 'browserify' && v !== 'native') { 1219 | _log("log", v + " => '" + _package.compiler[v] + "'"); 1220 | } 1221 | }); 1222 | 1223 | return obj; 1224 | } 1225 | --------------------------------------------------------------------------------