Compare commits

...

305 Commits

Author SHA1 Message Date
calebboyd cf23147c9c fix: windows build 10.16 2019-06-24 15:57:54 -05:00
calebboyd 9c1d67b2e6 build: add 3rd scheduled build 2019-06-24 14:20:51 -05:00
calebboyd 93e0a021d3 build: increase timeout to 3 hours 2019-06-24 11:37:23 -05:00
calebboyd 021f50d50f build: add schedules to yml 2019-06-24 09:38:05 -05:00
calebboyd bf5ff7d9bd chore: set build limit to two hours 2019-06-21 15:20:21 -05:00
calebboyd 86c3acedb1 chore: pipeline display names 2019-06-17 18:38:54 -05:00
calebboyd ecb3d41a97 build: exit on existing target 2019-06-17 12:57:35 -05:00
calebboyd 1eb44797d0 fix: don't expand argv1 on node 12 bootstrap 2019-06-17 12:49:28 -05:00
calebboyd 1023b72513 fix: update patches for node 12 bootstrapping 2019-06-12 16:04:17 -05:00
calebboyd 0cf436c9a7 chore: dep bump 2019-06-11 10:42:26 -05:00
calebboyd 77f49eb07a fix: asset alias 2019-06-11 10:36:09 -05:00
calebboyd 9bc98bf597 ci: fix gcc version 2019-06-10 15:50:26 -05:00
calebboyd 7e9a05675c fix: reference ogfs in readFilePath for internalmethods 2019-06-05 15:03:49 -05:00
calebboyd e62d728624 fix: uppercase windows path letters after normalize
closes #618
2019-05-28 18:04:16 -05:00
Jason Cooke a0c8d31f03 docs: fix typo (#625) 2019-05-23 18:17:55 -05:00
calebboyd 472b98d839 chore: publish copyFile shim 2019-04-28 14:28:59 -05:00
calebboyd 9ae6ed8ae8 chore: log open handles after a minute or two 2019-04-28 13:28:40 -05:00
calebboyd a0c648bc8b chore: set install directory for nasm 2019-04-26 14:33:26 -05:00
Daniel Ruf 463595f502 chore: typo 2019-04-25 11:23:08 -05:00
calebboyd be4ab09745 chore: update ci in readme 2019-04-25 11:22:19 -05:00
calebboyd a6ad034336 chore: set env variables 2019-04-18 13:27:08 -05:00
calebboyd 0afbe3b732 chore: fix types in azure build script 2019-04-15 23:03:41 -05:00
calebboyd 0865fda5ef fix: don't infer main module when not in tty 2019-04-15 23:01:50 -05:00
calebboyd 7bfdc39700 chore: add azure-pipeline.yml 2019-04-15 17:12:41 -05:00
calebboyd 9486399cec chore: update alpine build script 2019-04-15 14:59:57 -05:00
calebboyd 75bf6a3dc4 chore: naive copyFile patch 2019-04-14 21:37:06 -05:00
calebboyd 0dca0745ee feat(fs): add option to restore all but internal filesystem methods 2019-04-08 22:14:51 -05:00
Antonio Nuno Monteiro 6fc2ed9519 fix: snapshot gyp variable name after node 11 (#606)
this changed from `embed_script` to `v8_embed_script` after node 11
2019-04-08 21:14:16 -05:00
calebboyd 448f4270ea fix: set entrypoint correctly when outside a tty 2019-04-08 16:23:21 -05:00
calebboyd c9c9b5a0a3 fix: mount vfs relative to execPath instead of cwd 2019-04-08 11:10:33 -05:00
calebboyd d2756a5f75 fix: load implicit input if stdin is empty without a tty 2019-04-04 23:54:06 -05:00
calebboyd 255f3887c7 chore: remove unused methods 2019-04-04 19:37:30 -05:00
calebboyd 75ae41b7b9 chore: bump version 2019-04-04 09:48:17 -05:00
calebboyd 3692c142fb doc: describe mangle option 2019-04-04 09:47:20 -05:00
calebboyd af2e381ed4 feat: ensure output directory exists
closes #590
2019-04-04 09:35:15 -05:00
jerry_liu 44393a22da More flexiable nexe footer search range to fix digital sign binary launch issue
Fix #372
2019-04-03 13:37:31 -05:00
jerry_liu 57a08d2cfd remove compress option 2019-04-03 08:15:21 -05:00
calebboyd 6825cef609 feat: asset option 2019-04-03 08:12:26 -05:00
calebboyd b3c2d2e764 fix: use hardcoded version for asset url 2019-04-01 16:38:47 -05:00
calebboyd d73325ba35 chore: analyze resources as entrypoints 2019-03-30 20:18:41 -07:00
calebboyd 6e3cadbe22 chore: dep bump 2019-03-30 13:54:51 -07:00
calebboyd de42dd94ad refactor: run download for src and prebuilt before plugins or patches 2019-03-30 13:29:58 -07:00
calebboyd 37192e4ca8 fix: ignore node cli for 11.6.0+ 2019-03-28 19:19:03 -07:00
calebboyd c5cdbd9380 chore: remove unused directories in appveyor 2019-03-09 15:27:25 -06:00
calebboyd b022cd525d fix: size on bundled stat 2019-02-21 22:33:44 -06:00
bruce-one 72d9dfebf6 fix(fs): stat file and directories for bundled files (#589)
Ensure Stat return has correct prototype.
In particular to fix `Reflect.apply(Stats.prototype.isFile, stat, [])`.
2019-02-21 18:48:49 -06:00
calebboyd 8849ac0c0c chore: don't try to upload an existing asset 2019-01-31 19:56:36 -06:00
calebboyd 8ad6641278 chore: update travis vm 2019-01-31 17:54:10 -06:00
calebboyd 1a75bdb058 fix: avoid syncronous errors in compile method 2019-01-23 20:31:11 -06:00
calebboyd 12c77a34fd chore: update resolve-dependencies 2019-01-05 11:25:36 -06:00
calebboyd c76edb7c0b chore: expose build on compiler 2018-12-31 08:51:53 -06:00
calebboyd 09d7093295 fix: ignore parsing errors in node_modules 2018-12-21 19:58:34 -06:00
calebboyd 3a091f45bd refactor(fs-patches): don't destroy arguments for non bundled files 2018-12-15 16:38:32 -06:00
bruce-one 9575c0811b refactor: avoid checking DEBUG every log, if set. (#562) 2018-12-05 19:51:04 -06:00
calebboyd ab6b540e23 fix: filter parser warnings 2018-11-29 09:47:35 -06:00
calebboyd dcea374922 fix: input detection and validity
closes #556, #548, #547, #533
2018-11-28 20:25:59 -06:00
calebboyd b3b6c6b936 chore: use npm i 2018-11-21 21:42:22 -06:00
calebboyd 058c34ee8a chore: update node-cli patches for 10-10.9 2018-11-21 18:31:06 -06:00
calebboyd 9750c70873 chore: update circle-ci caching 2018-11-21 10:58:03 -06:00
calebboyd 7bc3a83667 fix: node 10 cli args 2018-11-20 14:35:35 -06:00
calebboyd aa93ab6516 chore: update resolve-dependencies 2018-11-15 14:27:36 -06:00
calebboyd c681a62566 chore: log json error response 2018-10-25 10:06:05 -05:00
calebboyd 2ea0f897d5 chore: bump version 2018-10-23 10:23:34 -05:00
António Nuno Monteiro cfa3036694 feat: invalidate binary cache when snapshot file changes (#542) 2018-10-23 09:31:17 -05:00
calebboyd c3ba3ec872 chore: bump version 2018-10-21 12:25:35 -05:00
António Nuno Monteiro 142338ede6 fix: stringify compiler.entrypoint in shims (#541) 2018-10-21 12:00:06 -05:00
calebboyd e3f1d6fc74 fix: missing cwd on resolve 2018-10-20 11:31:05 -05:00
calebboyd 0db8122f8f fix: handle configure script in node 10.11+ 2018-10-20 11:23:12 -05:00
António Nuno Monteiro 129c2f86a0 fix: include snapshot in patches (#539) 2018-10-20 11:16:41 -05:00
calebboyd db766e2832 fix: use vcBuild and make based on current platform 2018-10-20 10:33:39 -05:00
calebboyd e6a416cc4f fix: add internalModuleReadJSON patch 2018-10-17 21:56:37 -05:00
calebboyd 35f4811826 fix: remove spread operator 2018-10-09 14:07:32 -05:00
calebboyd 891ac0ab2a chore: badges 2018-10-04 08:20:00 -05:00
calebboyd 18bbf24456 chore: bump version 2018-10-04 08:20:00 -05:00
calebboyd 689160e833 fix: handle empty content when adding an empty file 2018-10-04 08:20:00 -05:00
calebboyd bd3ae2e80a chore: disable stdin in docker build 2018-10-04 08:20:00 -05:00
calebboyd 0447d8004c chore: append build error to log file 2018-10-04 08:19:59 -05:00
Jon Tore Hafstad 11be5a2573 chore(docs): resource should always we a glob string (#528)
To avoid confusion regarding the resource glob string, all examples i the readme should be in strings.
2018-10-04 08:19:03 -05:00
calebboyd 664dc6a11b chore: update arm version generator 2018-09-26 09:57:08 -05:00
calebboyd 4a7bfd99b6 chore: fix platform check 2018-09-25 11:48:39 -05:00
calebboyd a3af96eae8 chore: update travis auth 2018-09-25 11:36:09 -05:00
calebboyd 8e41015c8d chore: 3.0 beta 2018-09-24 14:13:21 -05:00
calebboyd 8382a2c1b3 fix: don't use typescript for bootstrap patch 2018-09-24 11:41:36 -05:00
calebboyd d385923c5a test: fix folder inference on circle ci 2018-09-24 11:28:49 -05:00
calebboyd 50ebc4fc32 chore: bump dependencies 2018-09-24 11:28:49 -05:00
calebboyd 53ef02f87a feat: standalone bundler 2018-09-24 11:28:49 -05:00
calebboyd c70d93b72f chore: setup nexe-fs as a standalone shim 2018-09-24 11:28:49 -05:00
calebboyd d4a194b239 fix: stdin detection 2018-09-24 11:28:49 -05:00
calebboyd dbcbadf9a5 chore: cleanup imports 2018-09-24 11:28:49 -05:00
calebboyd c3a2fd9eb4 fix(patches): third_party_main file patch regression 2018-09-24 11:28:49 -05:00
calebboyd b6dd232e7a chore: bump next 2018-09-24 11:28:49 -05:00
calebboyd f69b219750 chore: bump resolve-dependencies 2018-09-24 11:28:49 -05:00
calebboyd ad41f07d85 chore: support node 10 2018-09-24 11:28:49 -05:00
calebboyd 5ac93b43ec fix: set input with module resolve 2018-09-24 11:28:49 -05:00
calebboyd 54212cbc52 chore: provide a helpful error when the base build is incorrect
closes #468
2018-09-24 11:28:49 -05:00
calebboyd f4d3ce954a chore: ensure capital drive letter in file manifest 2018-09-24 11:28:49 -05:00
calebboyd b8522b6310 fix: error on missing entry
closes #467

closes #464
2018-09-24 11:28:49 -05:00
calebboyd d6d81b9e23 fix: bump resolve-dependencies and use expand option 2018-09-24 11:28:49 -05:00
James M. Greene 34f3ddc26a fix: bootstrap file path in node 4.x 2018-09-24 11:28:49 -05:00
calebboyd 8407f0fc38 chore: update npm in ci 2018-09-24 11:28:49 -05:00
calebboyd a4f4f3edf8 chore: fix ci commands 2018-09-24 11:28:49 -05:00
calebboyd babd6ac328 chore: add fs exists 2018-09-24 11:28:49 -05:00
calebboyd 56d2b9ab53 chore: remove outdated examples 2018-09-24 11:28:49 -05:00
calebboyd 48b61442db docs: add ico --build note 2018-09-24 11:28:49 -05:00
calebboyd b61c0df51d fix: non-strict dependency resolution 2018-09-24 11:28:49 -05:00
calebboyd 92058b6137 chore(bundling): start alternate bundling strategy 2018-09-24 11:28:49 -05:00
calebboyd ce9cdcba46 feat(releases): add arm7l architecture as buildable releases 2018-09-24 11:28:49 -05:00
calebboyd f9bc43a03b chore(ci): properly check version for appveyor matrix builds 2018-08-01 09:27:16 -05:00
calebboyd e84e236a3d chore(ci): set correct version on windows 2018-07-31 22:02:57 -05:00
calebboyd 8fd84fd34d chore(ci): log currently building version 2018-07-31 21:53:57 -05:00
calebboyd 901c51d6ba chore(ci): fix artifact path 2018-07-31 21:01:06 -05:00
calebboyd 9190b0df77 chore: fix build blacklist 2018-07-31 18:03:35 -05:00
calebboyd 687c2e8cfc chore(ci): use appveyor build matrix for handling > 10 builds 2018-07-31 18:00:37 -05:00
calebboyd 4eeeb55706 chore(ci): remove verbose windows logging 2018-07-31 17:35:19 -05:00
calebboyd c6d9cd92ad chore: add build image to appveyor 2018-07-31 17:32:38 -05:00
calebboyd c1d7e6a406 chore(ci): add nasm to appveyor build 2018-07-31 17:18:14 -05:00
calebboyd 86c919aac5 chore: verbose logging on windows build 2018-07-31 16:39:20 -05:00
calebboyd 2271e426e2 fix: encoding options
closes #510
2018-07-31 14:48:40 -05:00
calebboyd bf65477ca3 fix: createReadStream off by 1 error 2018-07-31 14:07:21 -05:00
calebboyd db7cdf0200 chore(ci): fix automated mac builds 2018-07-31 11:43:36 -05:00
calebboyd ea93e16f40 chore: update shim-fs 2018-07-31 11:32:34 -05:00
Caleb Boyd 966f8b4d51 chore: update node on travis 2018-07-26 07:47:20 -05:00
Jared Allard f77ed75603 feat: slightly better issue template (#513) 2018-07-26 07:46:45 -05:00
calebboyd 4fe39d4b68 chore: remove appveyor cache 2018-07-15 17:09:04 -05:00
calebboyd 91033c284a chore: bump version 2018-07-12 08:46:01 -05:00
bruce-one 5c4ea55b13 fix: ensure setupManifest is called for stat functions. (#501) 2018-07-12 08:43:39 -05:00
calebboyd 7768567380 chore: bump version 2018-06-12 23:02:50 -05:00
calebboyd 41578e0115 fix: only use stdin if input is not passed
closes #467
2018-06-12 23:01:07 -05:00
calebboyd 089aa02a82 fix: always assign env 2018-06-01 21:37:08 -05:00
Jared Allard 92fa19cba6 chore(ci): now using circleci 2.0 (#487) 2018-05-23 23:06:17 -05:00
calebboyd 8dd5cf1973 chore: bump version 2018-05-08 11:49:45 -07:00
James M. Greene 24c0b6be76 fix: case-sensitive windows PATH setting for python configuration (#481)
fixes #474
amends #476

related to nodejs/node#20605
2018-05-08 11:47:16 -07:00
James M. Greene e65ce4df11 fix: windows PATH setting for python configuration (#476)
fixes #474
2018-05-05 10:08:57 -05:00
calebboyd b91576973d chore: bump appveyor node version 2018-03-26 10:21:55 -05:00
calebboyd 9a9511a429 chore: bump version 2018-02-23 00:38:53 -06:00
foxever 4cf87e68e6 fix: pifi -> pify, and optional uglify-es in package.json (#463)
closes #462
2018-02-23 00:34:32 -06:00
calebboyd 66afccc2e2 fix: order of operations #448 2018-02-16 22:26:42 -06:00
David Goemans 71e77052e5 docs: add note about --build required for --ico option 2018-02-07 11:54:26 -06:00
calebboyd eef430afb1 chore: update fuse-box 2018-02-05 20:22:23 -06:00
bruce-one 133d1919fa feat: allow setting NODE_OPTIONS. (#448) 2018-02-01 16:29:49 -06:00
calebboyd 9a81cc2fa2 fix: don't rely on got as a transient dependency
closes #439
2018-01-22 18:37:50 -06:00
Bryce Gibson a40d697542 feat: add createReadStream support for resources 2018-01-22 19:24:13 -05:00
calebboyd 02481f069c fix re-order shim 2018-01-04 13:44:01 -06:00
Andrew Razumovsky cb26021296 fix(rc): regex works to the end of the line (#437) 2017-12-21 22:47:30 -06:00
calebboyd 719804eb2f chore: bump dependencies 2017-12-02 12:52:59 -06:00
calebboyd 721b06f5d2 chore: bump rc 23 2017-11-29 15:52:53 -06:00
calebboyd c1896e13a3 feat: add proxy support (set environment variable HTTPS_PROXY) 2017-11-29 15:49:33 -06:00
calebboyd 931af404ae chore: bump rc 2017-11-26 09:10:04 -06:00
Tim f83298a0af fix(download): remove the ghToken for binary download
The github token is only needed for the API access and will confuse amazon S3 when it is passed to the download:

```
<?xml version="1.0" encoding="UTF-8"?>\n<Error><Code>InvalidArgument</Code><Message>Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified</Message><ArgumentName>Authorization</ArgumentName><ArgumentValue>token herebesecrettoken</ArgumentValue><RequestId>7CCDB87CF1347C29</RequestId><HostId>xyyeVP5ImwDePFMXAZheNYLnXQBsVCka4U6KLWKCjynHNpQZxzfBdl4sRfUbORSul8c8YNwux8w=</HostId></Error>
```

Refs #425
2017-11-26 08:08:28 -07:00
calebboyd 717e825b17 chore: lint 2017-11-25 21:53:16 -06:00
calebboyd 5ac0afb0dd feat(options): add ability to pass a github token 2017-11-25 21:51:41 -06:00
Ishai Jaffe 23f550818b fix(shim): properly serialize path 2017-11-14 14:57:39 -05:00
calebboyd 3d18eae18a chore: bump rc 2017-10-27 21:21:47 -05:00
Artem Derevnjuk 6f553d0fba fix(embed-node): import missing module to work with fs (#416) 2017-10-27 21:17:26 -05:00
calebboyd aba3047191 fix: only add extension when targeting windows 2017-10-25 23:35:26 -05:00
calebboyd 52b58a107d feat: enable fake-argv by default 2017-10-21 22:48:42 -05:00
calebboyd ec734e4fb1 chore: use yarn in circle-ci 2017-10-20 17:38:20 -05:00
calebboyd 7c755e666f chore: bump fuse-box 2017-10-17 16:56:44 -05:00
calebboyd 90ae7de7ac chore: bump fuse-box 2017-10-16 13:02:08 -05:00
Alexis Tyler 96f67ecba4 chore(docs): fix typo & add missing period 2017-10-15 19:32:12 -05:00
calebboyd 6526a611a8 chore: bump deps 2017-10-10 09:56:22 -04:00
calebboyd f9bf4ca464 chore: add chat badge 2017-10-07 09:27:30 -05:00
calebboyd 4ec8a43d60 fix: consistent cwd usage 2017-10-05 20:54:37 -05:00
calebboyd 77109235c4 chore: separate bindings and .node embedding 2017-10-05 18:58:41 -05:00
Bryce Gibson c1531cbf23 Pause logger before and resume after spawn. 2017-10-04 22:22:53 -05:00
Bryce Gibson c5f3116c18 Show process output when loglevel is verbose. 2017-10-04 22:22:53 -05:00
calebboyd 03e2985c7a feat: stat and statSync 2017-10-03 01:54:06 -05:00
calebboyd a4b48611d9 feat: readdir and readdirSync 2017-10-03 00:39:33 -05:00
calebboyd 83d3ccf1c5 feat: support the cluster module 2017-10-02 22:28:04 -05:00
calebboyd 356d4c1886 fix(nix): only make output executable 2017-10-02 21:49:05 -05:00
calebboyd b74bdfc53b chore: fusebox beta 2017-10-02 18:04:32 -05:00
calebboyd 927820a5ec chore: service progress 2017-10-02 18:02:32 -05:00
calebboyd 4ef98bdc8a chore: re-order docs 2017-09-30 15:44:57 -05:00
calebboyd 90d7beb4f3 docs: add native module note 2017-09-30 15:28:03 -05:00
calebboyd 55c95b4663 docs: elaborate on target 2017-09-30 15:06:34 -05:00
calebboyd fe5aa0be09 fix: dequote 2017-09-29 23:16:09 -05:00
calebboyd 2a2526d3bc fix: disable stdin for programatic usage 2017-09-29 17:49:17 -05:00
calebboyd c8310ce3bc docs: update readme 2017-09-28 22:02:27 -05:00
calebboyd bb356a967b chore: multi-arch builds on travis 2017-09-28 16:06:55 -05:00
calebboyd 0d62d0100d chore: bump rc.9 2017-09-28 00:15:44 -05:00
calebboyd bfd07cd10a chore: move mac build to circle 2017-09-27 23:36:52 -05:00
calebboyd 4390568654 fix: ia32 target normalization 2017-09-27 01:34:07 -05:00
calebboyd 5c9cf3580c chore: update build 2017-09-27 00:47:16 -05:00
calebboyd fcb54d9085 fix: check error code on build commands 2017-09-26 23:54:18 -05:00
calebboyd 11fa087e9d refactor: move startup scripts to shims, add plugins 2017-09-26 20:16:03 -05:00
calebboyd b822984776 fix: input path for bundle 2017-09-26 00:10:09 -05:00
calebboyd 4f2b1c51d5 refactor: addResource compiler method 2017-09-26 00:10:09 -05:00
Brian McCallister dbcff4c7d2 fix: minor spelling problem 2017-09-21 18:04:07 -05:00
calebboyd 9906df8d55 chore: bump rc6 2017-09-13 19:08:44 -05:00
calebboyd d685ec21e5 fix: custom bundle resolution 2017-09-13 19:07:54 -05:00
calebboyd a2c4f3e9f1 fix: use cwd directory name for output resolution before timestring 2017-09-13 18:43:26 -05:00
calebboyd 79bcb753b4 fix: native module fuse plubin 2017-09-11 01:20:37 -05:00
calebboyd bc2a637cc4 docs: cleanup 2017-09-10 20:18:37 -05:00
calebboyd ad279e6daf docs: clarify resource and build documentation 2017-09-09 14:08:04 -05:00
calebboyd 2b9a8b1f0e feat: fake-argv option 2017-09-09 02:02:52 -05:00
calebboyd 7a7aaefa23 fix: set input before name (so name can derive from input) 2017-09-08 23:44:26 -05:00
calebboyd 04fdd165a4 feat: productversion and fileversion replacements in node-rc 2017-09-08 14:29:19 -05:00
calebboyd 455d6893fd fix: cache for build vs target 2017-09-08 08:13:40 -05:00
calebboyd ee529ce9b5 chore: fix target parsing 2017-09-07 22:56:30 -05:00
calebboyd 280b93b0a8 chore: add debugBundle to write out bundle 2017-09-05 23:41:25 -05:00
calebboyd 9ecd84ff8f chore: update fuse-box in example 2017-09-05 23:01:23 -05:00
calebboyd eaaffc7221 chore: rc2 2017-09-04 14:46:06 -05:00
calebboyd 75651af5f9 chore: publish beta 2017-09-04 14:46:06 -05:00
calebboyd c0b6575da0 feat: build-target options 2017-09-04 14:46:06 -05:00
calebboyd a9de25ba1f docs: update readme with new usage 2017-09-04 14:46:06 -05:00
calebboyd c7af89f12c fix: don't call next twice 2017-09-04 14:46:06 -05:00
calebboyd 7d0cdd7a50 feat: targets 2017-09-04 14:46:06 -05:00
calebboyd 9e5ab3e099 chore: options, and resource normalization 2017-09-04 14:46:06 -05:00
calebboyd c6d057ac43 feat: initial build 2017-09-04 14:46:06 -05:00
calebboyd 5df6840972 chore: inject source-map-support 2017-09-04 14:46:06 -05:00
calebboyd a0d859921d feat: initial fuse-box bundling support 2017-09-04 14:46:06 -05:00
calebboyd fcd803686c chore: test appveyor build 2017-08-24 00:12:18 -05:00
calebboyd ba78c7dc54 refactor: append in lieu of inserting payload 2017-08-23 22:35:01 -05:00
Jared Allard 430d63e7eb readme: update build link 2017-08-14 14:43:00 -07:00
calebboyd 3e435ecbf8 chore: beta 6 2017-08-13 17:34:54 -05:00
David Lechner aeefcbeba3 chore: add newline to end of command line help text (#361)
Without this, when you run `nexe --help` in a terminal, your shell prompt will be on the same line as `option can be used more than once`.
2017-08-13 16:20:23 -05:00
calebboyd 563aa97a89 fix: add bundle directly to third_party_main 2017-08-13 15:28:32 -05:00
calebboyd 88a46dc730 Merge branch 'cb-wip' 2017-08-03 23:00:51 -05:00
calebboyd 1a6b113d6e chore: wip bundling 2017-08-03 22:59:15 -05:00
calebboyd d375c8f214 chore: indicate beta in readme 2017-07-20 16:20:40 -05:00
calebboyd 15d919c302 chore: docs and ts fixes 2017-07-18 11:08:45 -05:00
calebboyd 6adf3c82c3 chore: update fusebox example version 2017-07-17 09:38:40 -05:00
calebboyd 73af22cc92 chore: publish nexe@beta 2017-07-16 22:43:02 -05:00
calebboyd 844d34b29a chore(readme): remove bundling point 2017-07-16 22:39:16 -05:00
calebboyd 548e6454ef chore: typescript and native module bundles 2017-07-16 21:40:23 -05:00
calebboyd ce2c4cefd0 wip: bundling work 2017-07-16 21:40:23 -05:00
James M. Greene ac64063916 Note the minimum Node.js engine required
Fixes #341
2017-07-15 20:01:56 -05:00
James M. Greene 249ee50130 README clarifications and consistency polishing 2017-06-29 11:46:14 -05:00
James M. Greene 76ec6a8be3 Prepend the Python path for Windows for overriding 2017-06-28 17:57:09 -05:00
calebboyd cac2759d3a chore: pr feedback 2017-06-26 20:01:53 -05:00
calebboyd 811f460763 feat: resources 2017-06-26 20:01:53 -05:00
calebboyd e12eb8b289 feat: instant builds 2017-06-26 20:01:53 -05:00
calebboyd 1f35fe498e chore: switch to tar-fs for better stream support 2017-06-26 20:01:53 -05:00
calebboyd ca7874c854 chore: misc cleanup 2017-06-26 20:01:53 -05:00
calebboyd 95e7971a96 chore: remove compiled files from repo 2017-06-26 20:01:53 -05:00
calebboyd f8151854f1 chore: update yarn.lock 2017-06-26 20:01:53 -05:00
calebboyd ac4ca3fe56 chore: default exports 2017-06-26 20:01:53 -05:00
calebboyd 0d67b1dd4c chore: babel + standard 2017-06-26 20:01:53 -05:00
calebboyd b10b3d268d fix: ensure python path is qouoted on windows #210 2017-06-26 20:01:53 -05:00
calebboyd 40f1e6056f feat: add isNexe which detects a nexe environment 2017-06-26 20:01:53 -05:00
calebboyd ec57ed508f fix: add missing depenencies 2017-06-26 20:01:53 -05:00
calebboyd 7668d7b471 feat: next 2017-06-26 20:01:53 -05:00
calebboyd 72afbe68a4 chore: add issue template 2017-04-10 23:29:42 -05:00
Jared Allard ad83cf769f fix: center text below badges 2017-03-06 12:43:33 -08:00
Jared Allard 8395f588a3 readme: add build status, support. 2017-03-05 22:07:23 -08:00
Jared Allard df3d667e4e update: A few new readme tweaks. 2017-03-05 21:57:40 -08:00
Jared Allard f928f3e758 fix: .gitignore ignore .DS_Store, add yarn.lock 2017-03-05 21:09:09 -08:00
Jared Allard 2f4356d6d2 Merge pull request #269 from anmonteiro/pythonpaths
startupSnapshot: need to generate python raw strings because of windows paths
2016-11-29 12:21:50 -08:00
Jared Allard 5cd8b5f177 Merge pull request #278 from kevgo/patch-1
Match CircleCI badge style to other badges
2016-11-29 12:21:26 -08:00
Kevin Goslar de58ad2593 Match CircleCI badge style to other badges 2016-11-29 07:20:14 -08:00
Christopher Karper b822f58973 Merge pull request #275 from control-freak-ide/master
=fixed toAbsolute for exists check instead of joining blindly
2016-11-21 10:11:30 -05:00
Christopher Karper de23c5e4c8 Merge pull request #236 from varatep/master
fix typo
2016-11-21 10:08:51 -05:00
mc007 8f96b2bca9 =fixed toAbsolute for exists check instead of joining blindly 2016-11-05 16:44:54 +01:00
António Nuno Monteiro 968d68b218 startupSnapshot: need to generate python raw strings because of windows
paths
2016-10-27 13:24:24 +02:00
Jared Allard 5136d3ebe9 Merge pull request #263 from anmonteiro/snapshotv1
allow custom V8 startup snapshot
2016-10-18 17:25:34 -07:00
António Nuno Monteiro 6a52ab861d allow custom V8 startup snapshot 2016-10-17 13:06:21 +02:00
Jared Allard 9b2b340f6d Merge pull request #244 from thekiba/issue-243
#243 - hotfix call browserify.require(file, opts)
2016-08-23 14:33:15 -07:00
TheKiba 03374f9f47 Added global variable isNexe 2016-08-11 21:08:12 +02:00
TheKiba 5d56a05531 This commit fixes #243 - hotfix call browserify.require(file, opts) 2016-08-06 02:20:44 +02:00
Jared Allard c113fbc731 Merge pull request #239 from ProfessorEugene/issue-238
#238 - attempting to have nodeConfigureArgs picked up
2016-07-24 23:24:00 -07:00
erachitskiy 1e2522852f This commit fixes #238 - attempting to get nodeConfigureArgs picked up regardless of python binary being used 2016-07-13 18:19:18 -04:00
Varatep Buranintu f6b8affdbf fix typo
var2.js --> ver2.js
2016-06-22 06:52:31 -07:00
Jared Allard 2b027229f1 Merge pull request #230 from jpheur/master
Fixes "Native module not found" problem
2016-05-30 09:47:41 -07:00
jpheur 970817e0f4 Fixes "Native module not found" problem 2016-05-30 12:05:43 +03:00
Jared Allard 23947c6d80 Merge pull request #214 from silviom/master
makeVCBuildArgs option added
2016-05-05 14:42:35 -07:00
Silvio Massari 88e5e05d49 makeVCBuildArgs option added 2016-05-05 14:38:28 -03:00
Jared Allard 541765e1d1 Merge pull request #211 from Apercu/patch-1
fix(exe): remove assignation on undefined variable
2016-04-16 18:50:08 -07:00
Balthazar Gronon 0c401f3c1e fix(exe): remove assignation on undefined variable 2016-04-16 18:04:41 +02:00
Jared Allard f6a18e18db npm/exe: check for lib/internal/boostrap_node.js, npm 1.1.2 fixes #207 2016-04-08 09:49:28 -07:00
Jared Allard da7b6af0fa Merge pull request #190 from ruffrey/patch-1
Document resourceRoot option
2016-02-28 22:23:02 -08:00
Jeff H. Parrish 694602fa22 Document resourceRoot option 2016-02-28 21:30:11 -08:00
Jared Allard 7994dc56c4 readme: remove yet another trace of the merge 2016-02-25 10:55:54 -08:00
Jared Allard f8b9de750f tests: actually output from the right command 2016-02-24 22:03:12 -08:00
Jared Allard cfd01c60c6 tests: output output for debug for now 2016-02-24 21:57:28 -08:00
Jared Allard 4899c1983b test: timeout for compile 2016-02-24 21:34:42 -08:00
Jared Allard 040e3c39a6 test: circle.yml 2016-02-24 21:28:14 -08:00
Jared Allard ebdd8055bd npm: update yargs to 4.2.0 2016-02-24 18:58:49 -08:00
Jared Allard 220836ea2f test: add cicircle, testing compilier toolchain. 2016-02-24 18:38:29 -08:00
Jared Allard 599858f799 test: run npm install on each test before trying to compile 2016-02-24 16:21:30 -08:00
Jared Allard b526d16298 test: log node version to stdout for circleci issues 2016-02-24 15:56:06 -08:00
Jared Allard d9d43332c6 test: ignore ci files 2016-02-24 15:47:48 -08:00
Jared Allard 42314a7083 test: setup mocha-circlci-reporter 2016-02-24 15:47:24 -08:00
Jared Allard f690b81a97 tests: finalize test system for now, be ready for a ci someday 2016-02-24 15:39:59 -08:00
Jared Allard 6faed1838f test: track tets/{mocha.opts, test.js} 2016-02-24 14:28:55 -08:00
Jared Allard 841f22f0bb tests: fix incorrect write type for express-test, exit process 2016-02-24 13:22:21 -08:00
Jared Allard ab1d92cf68 exe: fix an issue where finalContents isnt initialized, more test work 2016-02-24 12:58:13 -08:00
Jared Allard 208c2b2b56 bundle: add _process instead of process to igv, start test system work 2016-02-24 00:00:46 -08:00
Jared Allard 3b25e7409b bundle: add process to global vars, may fix some issues 2016-02-23 23:34:08 -08:00
Jared Allard aa6988c85f npm: vbump to 1.1.1 for when its released 2016-02-23 23:15:41 -08:00
Jared Allard a8c13a436b bundle: browserify.paths for controling require paths from nexe 2016-02-23 23:14:57 -08:00
Jared Allard f50e94a781 bundle/exe: more ES6, browserify.excludes, browserify.requires in package.json. 2016-02-23 23:00:19 -08:00
Jared Allard 71f39e5a11 Merge pull request #188 from jeff-blaisdell/issue-184
ISSUE-184: Allow browserify configuration to be passed through to bundler.
2016-02-22 09:08:58 -08:00
Jeff Blaisdell ae4baad4f3 ISSUE-184: Allow browserify configuration to be passed through to bundler. 2016-02-21 09:58:44 -06:00
Jared Allard 0ffbb5be66 Merge pull request #181 from mt-sergio/patch-1
Missing dot
2016-02-08 06:03:07 -08:00
Sergio Arcos 97f6f71788 Missing dot 2016-02-08 17:03:27 +09:00
Jared Allard 6a07ea62ea exe: support --jsFlag, use browserify API, try to get work started on source maps 2016-02-07 00:45:16 -08:00
Jared Allard ce2db38b92 npm: 1.1.0 release 2016-02-01 12:57:14 -08:00
Jared Allard f75948b7e8 exe: use custom tar+gz extraction method, should fix #175 2016-02-01 12:56:48 -08:00
Jared Allard 36a31b1048 git/npm: dont try to include src directory. 2016-01-30 15:13:42 -08:00
Jared Allard b25643483e npm: dont track test 2016-01-29 11:40:00 -08:00
Jared Allard 3f997ca93a npm: lets go npm! 2016-01-29 11:39:10 -08:00
Jared Allard 6273bd2e9a npm: continue the attempts to figure out why bin isnt included 2016-01-29 11:38:38 -08:00
Jared Allard 4d8a1849e8 npm: try to fix bin/nexe not being included in npm 2016-01-29 11:21:14 -08:00
Jared Allard df246cd2fc exe: support 0.10.x for js-flags 2016-01-28 20:21:26 -08:00
Jared Allard ea38821fd4 test: include js-yaml senario, which fixes #148 2016-01-28 17:51:56 -08:00
70 changed files with 6982 additions and 1578 deletions
+8
View File
@@ -0,0 +1,8 @@
root = true
[*]
end_of_line = lf
charset = utf-8
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
+11 -10
View File
@@ -1,12 +1,13 @@
node_modules
.DS_Store .DS_Store
*.nex temp/
src coverage
nexe.exe
nexe nexe
npm-debug.log node_modules
test/**/src *.log
test/**/out.nex *.exe
test/**/test.nex *.bin
test/**/tmp !winsw.exe
test/**/node_modules .idea
.nexe
lib
+3
View File
@@ -0,0 +1,3 @@
{
"recommendations": [ "ms-vscode.vscode-typescript-tslint-plugin" ]
}
+18
View File
@@ -0,0 +1,18 @@
{
"tslint.enable": true,
"tslint.run": "onType",
"tslint.autoFixOnSave": true,
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true
},
"search.exclude": {
"lib/**/*": true,
"**/node_modules": true,
"**/bower_components": true
},
"typescript.tsdk": "node_modules\\typescript\\lib"
}
+39 -26
View File
@@ -1,51 +1,64 @@
# Nexe ChangeLog # Nexe ChangeLog
## 2017-05-29, Version v2.0.0-beta.1, @calebboyd
Nexe 2.0 is a rewrite to enable some new features. These include:
* Quick Builds!
* Userland build patches
* Resource storage/access rewrite
* stdin interface
* Optional, pluggable bundling
### Breaking Changes
* New options -- Please see the [readme](README.md#options)
* Bundling is no longer enabled by default
* To access included resources `fs.readFile` and `fs.readFileSync` should be used
## 2015-02-20, Version v0.3.7, @jaredallard ## 2015-02-20, Version v0.3.7, @jaredallard
### Noteable Changes ### Noteable Changes
* Fixed #103. * Fixed #103.
* Made not-available require not a fatal error. * Made not-available require not a fatal error.
* Stub and system to ignore certain requires. * Stub and system to ignore certain requires.
* Added 'sys' to ignore list. * Added 'sys' to ignore list.
* We have a gitter! * We have a gitter!
* Gave win32 a 100 length progress bar. * Gave win32 a 100 length progress bar.
### Commits ### Commits
* [**2cacd83**] Update README.md (@crcn) * [**2cacd83**] Update README.md (@crcn)
* [**0e90ac9**] Update README.md (@crcn) * [**0e90ac9**] Update README.md (@crcn)
* [**54967d1**] Added Gitter badge (@jaredallard) * [**54967d1**] Added Gitter badge (@jaredallard)
* [**bb489a3**] Fixes #98 by giving win32 a 100 length progress bar. (@jaredallard) * [**bb489a3**] Fixes #98 by giving win32 a 100 length progress bar. (@jaredallard)
* [**39665a8**] Lighter weight way to accomplish the exclusion of the sys module (@CKarper) * [**39665a8**] Lighter weight way to accomplish the exclusion of the sys module (@CKarper)
* [**5aca22d**] This handles embedding 'bin' scripts with shebang interpreter... (@CKarper) * [**5aca22d**] This handles embedding 'bin' scripts with shebang interpreter... (@CKarper)
* [**e79b0fb**] Stub to ignore require('sys') (@CKarper) * [**e79b0fb**] Stub to ignore require('sys') (@CKarper)
## 2015-02-15, Version v0.3.6, @jaredallard ## 2015-02-15, Version v0.3.6, @jaredallard
### Noteable Changes ### Noteable Changes
* Now support .json in require. * Now support .json in require.
* Fixed a major --python flag bug. * Fixed a major --python flag bug.
### Commits ### Commits
* [**cac6986**] V bump to solve critical error. (@jaredallard) * [**cac6986**] V bump to solve critical error. (@jaredallard)
* [**b040337**] Fixes #99, resolves #97 by warning on missing file. New examples... (@jaredallard) * [**b040337**] Fixes #99, resolves #97 by warning on missing file. New examples... (@jaredallard)
* [**ad4da1d**] Support .json extensions in require() resolution (@CKarper) * [**ad4da1d**] Support .json extensions in require() resolution (@CKarper)
## 2015-02-14, Version v0.3.5, @jaredallard ## 2015-02-14, Version v0.3.5, @jaredallard
### Noteable Changes ### Noteable Changes
* Added new flag: `--python </path/to/python>` * Added new flag: `--python </path/to/python>`
* Added resourceFiles option which allows embedding files and getting them via embed.js * Added resourceFiles option which allows embedding files and getting them via embed.js
* Updated a bunch of the dependencies. * Updated a bunch of the dependencies.
* `process.argv` is now consistent in a child fork case. * `process.argv` is now consistent in a child fork case.
* Added `child_process.fork` support. * Added `child_process.fork` support.
* Added new collaborators: * Added new collaborators:
* Jared Allard (@jaredallard) * Jared Allard (@jaredallard)
### Commits ### Commits
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2013 Craig Condon Copyright (c) 2013-2018 Nexe Contributors
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
+223 -163
View File
@@ -1,186 +1,246 @@
### Nexe <p align="center"><img src="https://cloud.githubusercontent.com/assets/2391349/23598327/a17bb68a-01ee-11e7-8f55-88a5fc96e997.png" /></p>
[![Join the chat at https://gitter.im/crcn/nexe](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jaredallard/nexe?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Dependency Status](https://david-dm.org/jaredallard/nexe.svg)](https://david-dm.org/crcn/nexe) <p align="center">
[![Stories in Ready](https://badge.waffle.io/jaredallard/nexe.svg?label=ready&title=Ready)](http://waffle.io/jaredallard/nexe) <a href="https://dev.azure.com/nexe-ci/Nexe/_build?definitionId=1"><img src="https://img.shields.io/azure-devops/build/nexe-ci/nexe/1/master.svg" alt="Build Status"></a>
<a href="https://www.npmjs.com/package/nexe"><img src="https://img.shields.io/npm/dt/nexe.svg" alt="Downloads"></a>
<a href="https://www.npmjs.com/package/nexe"><img src="https://img.shields.io/npm/v/nexe.svg" alt="Version"></a>
<a href="https://www.npmjs.com/package/nexe"><img src="https://img.shields.io/npm/l/nexe.svg" alt="License"></a>
</p>
Nexe is a command-line utility that compiles your Node.js application into a single executable file. <p align="center"><code>npm i nexe -g</code></p>
<p align="center">Nexe is a command-line utility that compiles your Node.js application into a single executable file.</p>
![screen shot 2014-02-28 at 10 32 54 am](https://f.cloud.github.com/assets/757408/2296993/c276f7b6-a0a6-11e3-86d3-e6c5feba2a85.png) <p align="center">
<img src="https://user-images.githubusercontent.com/5818726/30999006-df7e0ae0-a497-11e7-96db-9ce87ae67b34.gif"/>
</p>
## Motivation and Features
### Motivation - Self contained applications
- Ability to run multiple applications with *different* node.js runtimes. - Ability to run multiple applications with *different* node.js runtimes.
- Distributable binaries without needing node / npm. - Distribute binaries without needing node / npm.
- Starts faster. - Idempotent builds
- Start and deploy faster.
- Lockdown specific application versions, and easily rollback. - Lockdown specific application versions, and easily rollback.
- Faster deployments. - Flexible build pipeline
- Cross platform builds
## Building Requirements ## Usage
- Linux / Mac / BSD / Windows - Application entrypoint:
- Python 2.6 or 2.7 (use --python if not in PATH)
- Windows: Visual Studio 2010+
## Caveats `nexe my-app.js`
### Doesn't support native modules - stdin interface
- 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. `rollup -c | nexe --resource "./public/**/*" -o my-app.exe`
### Doesn't support dynamic require statements For more CLI options see: `nexe --help`
Such As: ### Examples
- `nexe server.js -r "public/**/*.html"`
- `nexe --build`
- `nexe -t x86-8.0.0`
## Resources
Additional files or resources can be added to the binary by passing `-r "glob/pattern/**/*"`. These included files can be read in the application by using `fs.readFile` or `fs.readFileSync`.
## Compiling Node
By default `nexe` will attempt to download a pre-built executable. However, It may be unavailable ([github releases](https://github.com/nexe/nexe/releases))
or you may want to customize what is built. See `nexe --help` for a list of options available when passing the [`--build`](#build-boolean) option. You will also need to ensure your environment is setup to [build node](https://github.com/nodejs/node/blob/master/BUILDING.md). Note: the `python` binary in your path should be an acceptable version of python 2. eg. Systems that have python2 will need to create a [symlink](https://github.com/nexe/nexe/issues/354#issuecomment-319874486).
## Node.js API
#### Example
```javascript ```javascript
var x = require(someVar); const { compile } = require('nexe')
compile({
input: './my-app.js',
build: true, //required to use patches
patches: [
async (compiler, next) => {
await compiler.setFileContentsAsync(
'lib/new-native-module.js',
'module.exports = 42'
)
return next()
}
]
}).then(() => {
console.log('success')
})
```
## NexeOptions
### `options: object`
- #### `input: string`
- Input bundle file path
- default: stdin or the current directory's main file (package.json)
- #### `output: string`
- Output executable file path
- default: same as `name` with an OS specific extension.
- #### `target: string | object`
- An object or string describing platform-arch-version. e.g. `'windows-ia32-10.13.0'`
- each segment is optional, and will be merged with the current environment
- Examples: ([full list](https://github.com/nexe/nexe/releases))
- `'win32-x86-10.13.0`
- `{ platform: 'alpine' }`
- `darwin-10.13.0`
- `linux-x64`
- `macos-10.13.0`
See [test/target.spec.ts](test/target.spec.ts)
- If the [`build`](#build-boolean) flag is set, the platform portion of the target is ignored.
- default: `process`
- #### `bundle: string | boolean`
- If a string is provided it must be a valid relative module path
and should provide an export with the following signature:
```typescript
export function createBundle (options: NexeOptions): Promise<string>
```
- default: true
- #### `name: string`
- Module friendly name of the application
- default: basename of the input file, or `nexe_${Date.now()}`
- #### `cwd: string`
- Directory nexe will operate on as though it is the cwd
- default: process.cwd()
- #### `mangle: boolean`
- If set to false, nexe will not include the virtual filesystem (your application and resources) on the output.
- This will cause the output to error as an "Invalid Binary" unless a userland patch alters the contents of lib/_third_party_main.js in the nodejs source.
- default: true
- #### `build: boolean`
- Build node from source, passing this flag tells nexe to download and build from source. Subsequently using this flag will cause nexe to use the previously built binary. To rebuild, first add [`--clean`](#clean-boolean)
- #### `asset: string`
- Provide a pre-built nexe binary asset, this can either be an http or https URL or a file path.
- default: `null`
- #### `python: string`
- On Linux this is the path pointing to your python2 executable
- On Windows this is the directory where `python` can be accessed
- default: `null`
- #### `flags: string[]`
- Array of node runtime flags to build node with.
- Example: `['--expose-gc']`
- default: `[]`
- #### `configure: string[]`
- Array of arguments for the node build configure step
- Example: `['--with-dtrace', '--dest-cpu=x64']`
- default: `[]`
- #### `make: string[]`
- Array of arguments for the node build make step
- default: `[]`
- #### `vcBuild: string[]`
- Options for windows build
- default: `['nosign', 'release']`
- #### `snapshot: string`
- path to a file to be used as the warmup snapshot for the build
- default: `null`
- #### `resources: string[]`
- Array of globs with files to include in the build
- Example: `['./public/**/*']`
- default: `[]`
- #### `temp: string`
- Path to use for storing nexe's build files
- Override in the env with `NEXE_TEMP`
- default: `~/.nexe`
- #### `ico: string`
- Path to a user provided icon to be used (Windows only). Requires `--build` to be set.
- #### `rc: object`
- Settings for patching the [node.rc](https://github.com/nodejs/node/blob/master/src/res/node.rc) configuration file (Windows only).
- Example (keys may vary depending on the version. Reference the file linked above):
```javascript
{
CompanyName: "ACME Corp",
PRODUCTVERSION: "17,3,0,0",
FILEVERSION: "1,2,3,4"
...
}
```
- default: `{}`
- #### `clean: boolean`
- If included, nexe will remove temporary files for the accompanying configuration and exit
- #### `enableNodeCli: boolean`
- Enable the original Node CLI (will prevent application cli from working).
- Node CLI arguments passed via the [NODE_OPTIONS](https://nodejs.org/api/cli.html#cli_node_options_options) environment
variable will still be processed. NODE_OPTIONS support can be disabled with the `--without-node-options` configure flag.
- default: `false`
- #### `fakeArgv: boolean`
- fake the entry point file name (`process.argv[1]`). If nexe was used with stdin this will be `'[stdin]'`.
- #### `ghToken: string`
- Provide a Github Token for accessing nexe releases
- This is usually needed in CI environments
- default: `process.env.GITHUB_TOKEN`
- #### `sourceUrl: string`
- Provide an alternate url for the node source code
- Note: temporary files will still be created for this under the specified version
- #### `loglevel: string`
- Set the loglevel, info, silent, or verbose
- default: `'info'`
- #### `patches: NexePatch[]`
- Userland patches for patching or modifying node source
- default: `[]`
- #### `plugins: NexePatch[]`
- Userland plugins for modifying nexe executable behavior
- default: `[]`
### `NexePatch: (compiler: NexeCompiler, next: () => Promise<void>) => Promise<void>`
Patches and Plugins are just a middleware functions that take two arguments, the `compiler`, and `next`. The compiler is described below, and `next` ensures that the pipeline continues. Its invocation should always be awaited or returned to ensure correct behavior. Patches also require that [`--build`](#build-boolean) be set, while plugins do not.
For examples, see the built in patches: [src/patches](src/patches).
### `NexeCompiler`
- `setFileContentsAsync(filename: string, contents: string): Promise<void>`
- Quickly set a file's contents within the downloaded Node.js source.
- `replaceInFileAsync(filename: string, ...replaceArgs): Promise<void>`
- Quickly perform a replace in a file within the downloaded Node.js source. The rest arguments are passed along to `String.prototype.replace`
- `readFileAsync(filename: string): Promise<NexeFile>`
- Access (or create) a file within the downloaded Node.js source.
- `addResource(filename: string, contents: Buffer): void`
- Add a resource to the nexe bundle
- `files: NexeFile[]`
- The cache of the currently read, modified, or created files within the downloaded Node.js source.
#### `NexeFile`
- `contents: string`
- `absPath: string`
- `filename: string`
Any modifications made to `NexeFile#contents` will be maintained in the cache _without_ the need to explicitly write them back out, e.g. using `NexeCompiler#setFileContentsAsync`.
## Native Modules
TODO
- [ ] Implement support for `node-pre-gyp#find`.
## Contributing
Building
```
$ git clone git@github.com:nexe/nexe.git
$ cd nexe
$ yarn
``` ```
In this case nexe won't bundle the file Testing
```javascript
var x;
if (someCheck) {
x = require("./ver1.js");
} else {
x = require("./var2.js");
}
``` ```
$ npm test
In this case nexe will bundle both files.
Workarounds:
1) for dynamic requires that you want bundled add the following into your project
```javascript
var dummyToForceIncludeForBundle = false;
if (dummyToForceIncludeForBundle) {
require("./loadedDynamicallyLater.js");
// ...
}
``` ```
this will trick the bundler into including them.
2) for dynamic files getting included that you don't want to be
```javascript
var moduleName = "./ver2.js";
if (someCheck) {
moduleName = "./ver1.js";
}
var x = require(moduleName);
```
Note: neither file will be bundled.
Using these two techniques you can change your application code so modules are not bundles, and generate a includes.js file as part of your build process so that the right files get bundled for your build configuration.
### &#95;&#95;dirname
Once the module is bundled it is part of the executable. &#95;&#95;dirname is therefore the executable dir (process.execPath). Thus if you put resources on a relative path from the the executable your app will be able to access them.
If you had a data file at `/dev/myNodeApp/stateManager/handler/data/some.csv`
and a file at `/dev/myNodeApp/stateManager/handler/loader.js`
```javascript
module.exports = fw.readFileSync(path.join(__dirname, "./data/some.csv"));
```
You would need to deploy some.csv in a sub dir `data/` along side your executable
There are potential use cases for &#95;&#95;dirname where the executable path is not the correct substitution, and could result in a silent error (possibly even in a dependency that you are unaware of).
Note: &#95;&#95;filename will be 'undefined'
### child_process.fork
child_process.spawn works is unmodified, but child_process.fork will make an attempt to launch a new instance of your executable and run the bundled module.
## Installation
Via NPM:
```bash
npm install nexe [-g]
```
Or git:
```bash
git clone https://github.com/crcn/nexe.git
```
### CLI Usage
```text
Usage: nexe -i [sources] -o [binary] [options]
Options:
-i, --input The entry javascript files [default: cwd]
-o, --output The output binary [default: out.nex]
-r, --runtime The node.js runtime to use [default: "latest"]
-t, --temp The path to store node.js sources [default: ./tmp/nexe]
-f, --flags Don't parse node and v8 flags, pass through app flags [default: false]
-v, --version Display version number
-p, --python Set path of python to use. [default: "python"]
-F, --framework Set the framework to use. [default: "nodejs"]
```
### Code Usage
```javascript
var nexe = require('nexe');
nexe.compile({
input: 'input.js', // where the input file is
output: 'path/to/bin', // where to output the compiled binary
nodeVersion: '5.5.0', // node version
nodeTempDir: 'src', // where to store node source.
nodeConfigureArgs: ['opt', 'val'], // for all your configure arg needs.
nodeMakeArgs: ["-j", "4"], // when you want to control the make process.
python: 'path/to/python', // for non-standard python setups. Or python 3.x forced ones.
resourceFiles: [ 'path/to/a/file' ], // array of files to embed.
flags: true, // use this for applications that need command line flags.
jsFlags: "--use_strict", // v8 flags
framework: "node" // node, nodejs, or iojs
}, function(err) {
if(err) {
return console.log(err);
}
// do whatever
});
```
### package.json inclusion
As of 0.4.0 you can now embed nexe options into package.json. Note that this Format
is still in works, so it is likely to change.
```json
"nexe": {
"input": "./bin/nexe",
"output": "nexe^$",
"temp": "src",
"runtime": {
"framework": "node",
"version": "5.5.0",
"js-flags": "--use_strict",
"ignoreFlags": true
}
}
```
Notes:
* output: can use ^$ for platform specific file extension
* js-flags: this is also known as v8 flags, and supports *all* v8 flags.
## Maintainers ## Maintainers
* __Jared Allard__ ([@jaredallard](https://github.com/jaredallard)) &lt;[jaredallard@outlook.com](mailto:jaredallard@outlook.com)&gt; (Active) [![Jared Allard](https://avatars.githubusercontent.com/u/2391349?s=130)](https://jaredallard.me/) | [![Caleb Boyd](https://avatars.githubusercontent.com/u/5818726?s=130)](https://github.com/calebboyd) | [![Dustin Greif](https://avatars.githubusercontent.com/u/3026298?s=130)](https://github.com/dgreif) |
* __Christopher Karper__ ([@ckarper](https://github.com/CKarper)) &lt;[Christopher.Karper@gmail.com](mailto:Christopher.Karper@gmail.com)&gt; (Active) ---|---|---|
* __Craig Condon__ ([@crcn](https://github.com/crcn)) &lt;[craig.j.condon@gmail.com](mailto:craig.j.condon@gmail.com)&gt; (Old Project Owner) [Jared Allard](https://github.com/jaredallard) | [Caleb Boyd](http://github.com/calebboyd) | [Dustin Greif](https://github.com/dgreif) |
### Former
- [Craig Condon](http://crcn.codes/)
+60
View File
@@ -0,0 +1,60 @@
schedules:
- cron: "0 8 * * *"
displayName: "Daily Build 1"
always: true
branches:
include:
- master
- cron: "0 13 * * *"
displayName: "Daily Build 2"
always: true
branches:
include:
- master
- cron: "0 19 * * *"
displayName: "Daily Build 3"
always: true
branches:
include:
- master
jobs:
- job: BaseBuild
timeoutInMinutes: 180
strategy:
matrix:
linux:
imageName: 'ubuntu-16.04'
mac:
imageName: 'macos-10.13'
windows_2017:
imageName: 'vs2017-win2016'
windows_2015:
imageName: 'vs2017-win2016'
alpine:
imageName: 'ubuntu-16.04'
pool:
vmImage: $(imageName)
steps:
- task: UsePythonVersion@0
displayName: Setup Python
inputs:
versionSpec: '2.7'
architecture: 'x64'
- task: NodeTool@0
displayName: Install Node
inputs:
versionSpec: '10.x'
- script: sudo apt-get update && sudo apt-get install -y libc6-dev-i386 gcc-multilib g++-multilib
condition: contains(variables['Agent.JobName'], 'linux')
displayName: Install Linux build dependencies
- script: choco install visualcpp-build-tools --version 14.0.25420.1 -fy && npm config set msvs_version 2015
condition: contains(variables['Agent.JobName'], 'windows_2015')
displayName: Install Windows build dependencies
- script: choco install nasm -fy
displayName: Install Windows Assembler (NASM)
condition: contains(variables['Agent.JobName'], 'windows')
- script: npm install && npm run ci:build
displayName: Build Nexe
env:
GITHUB_TOKEN: $(PersonalGithubToken)
NEXE_VERBOSE: $(NEXE_VERBOSE)
-96
View File
@@ -1,96 +0,0 @@
#!/usr/bin/env node
var path = require('path'),
fs = require('fs'),
nexe = require('../lib'),
_log = require("../lib/log"),
cli = require('yargs').
usage('Usage: $0 -i [sources] -o [binary]').
options('i', {
demand: true,
alias: 'input',
desc: 'The entry javascript files',
default: process.cwd()
}).
options('o', {
demand: true,
alias: 'output',
desc: 'The output binary',
default: "out.nex"
}).
options('r', {
alias: 'runtime',
default: 'latest',
description: 'The node.js runtime to use'
}).
options('t', {
alias: 'temp',
default: './tmp/nexe',
description: 'The path to store node.js sources'
}).
options('f', {
alias: 'flags',
description: 'Don\'t parse node and v8 flags, pass through app flags',
default: false
}).
options('v', {
alias: 'version',
description: 'Display version number'
}).
options('p', {
alias: 'python',
description: 'Set path of python to use.',
default: 'python'
}).
options('F', {
alias: 'framework',
description: 'Set the framework to use',
default: 'nodejs'
});
var argv = cli.argv;
if(argv.h || argv.help) {
cli.showHelp();
process.exit();
} else if (argv.v || argv.version) {
var pkginfo = require('../package.json');
console.log(pkginfo.version);
process.exit();
}
/**
* TODO: Support network shares?
**/
function toAbsolute(pt) {
if(pt.substr(0, 1) == "/") return pt;
if(pt.substr(1, 3) == ":/") return pt; // for windows "c:/"
return path.join(process.cwd(), pt);
}
if(fs.lstatSync(argv.i).isDirectory()) {
opts = nexe.package(path.join(argv.i, "package.json"), argv);
nexe.compile(opts, function(error) {
if(error) {
return console.log(error.message);
}
});
} else {
/**
* Call the core
**/
nexe.compile({
input : require.resolve(toAbsolute(argv.i)),
output : toAbsolute(argv.o),
flags : argv.f,
nodeVersion : argv.r,
python : argv.python,
nodeTempDir : toAbsolute(argv.t),
framework : argv.F
},
function(error) {
if(error) {
return console.log(error.message);
}
}
);
}
+8
View File
@@ -0,0 +1,8 @@
const express = require('express')
const path = require('path')
const app = express()
app.use('/', express.static(path.join(__dirname, 'public')))
app.listen(8888)
+373
View File
@@ -0,0 +1,373 @@
{
"name": "express-app",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
"requires": {
"mime-types": "~2.1.18",
"negotiator": "0.6.1"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"body-parser": {
"version": "1.18.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
"integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
"requires": {
"bytes": "3.0.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.1",
"http-errors": "~1.6.2",
"iconv-lite": "0.4.19",
"on-finished": "~2.3.0",
"qs": "6.5.1",
"raw-body": "2.3.2",
"type-is": "~1.6.15"
}
},
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
},
"content-disposition": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.16.3",
"resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz",
"integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
"requires": {
"accepts": "~1.3.5",
"array-flatten": "1.1.1",
"body-parser": "1.18.2",
"content-disposition": "0.5.2",
"content-type": "~1.0.4",
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.1.1",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.3",
"qs": "6.5.1",
"range-parser": "~1.2.0",
"safe-buffer": "5.1.1",
"send": "0.16.2",
"serve-static": "1.13.2",
"setprototypeof": "1.1.0",
"statuses": "~1.4.0",
"type-is": "~1.6.16",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"statuses": "~1.4.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
}
},
"iconv-lite": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
"integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
},
"mime-db": {
"version": "1.35.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz",
"integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg=="
},
"mime-types": {
"version": "2.1.19",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz",
"integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==",
"requires": {
"mime-db": "~1.35.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
"integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.8.0"
}
},
"qs": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
},
"range-parser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
},
"raw-body": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
"integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
"requires": {
"bytes": "3.0.0",
"http-errors": "1.6.2",
"iconv-lite": "0.4.19",
"unpipe": "1.0.0"
},
"dependencies": {
"depd": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
"integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
},
"http-errors": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
"integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
"requires": {
"depd": "1.1.1",
"inherits": "2.0.3",
"setprototypeof": "1.0.3",
"statuses": ">= 1.3.1 < 2"
}
},
"setprototypeof": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
"integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
}
}
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"send": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.6.2",
"mime": "1.4.1",
"ms": "2.0.0",
"on-finished": "~2.3.0",
"range-parser": "~1.2.0",
"statuses": "~1.4.0"
}
},
"serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.2",
"send": "0.16.2"
}
},
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
},
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.18"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"name": "express-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.16.3"
}
}
+1
View File
@@ -0,0 +1 @@
<h1>Hello World!</h1>
Executable
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env node
const options = require('./lib/options')
if (require.main === module) {
//fast path for help/version
const argv = options.argv
const eol = require('os').EOL
const showHelp = argv.help || argv._.some(x => x === 'help')
const showVersion = argv.version || argv._.some(x => x === 'version')
if (showHelp || showVersion) {
process.stderr.write(showHelp ? options.help : options.version + eol)
} else {
const nexe = require('./lib/nexe')
nexe.compile(argv).catch((error) => {
const NexeError = require('./lib/compiler').NexeError
const chalk = require('chalk')
const isSilent = Boolean(argv.silent === true || argv.loglevel === 'silent')
if (!isSilent) {
if (error instanceof NexeError) {
process.stderr.write(eol + chalk.red('Error: ') + error.message + eol
+ eol + 'See nexe -h for usage..' + eol + eol
)
} else {
process.stderr.write(error.stack + eol)
}
}
process.exit(1)
})
}
} else {
module.exports = require('./lib/nexe')
}
+19
View File
@@ -0,0 +1,19 @@
<!-- If you need help or think you have found a bug, please help us with your issue by entering the following information (otherwise you can delete this text): -->
**Is this a BUG or a FEATURE REQUEST?:**
**What happened:**
**What you expected to happen:**
**How to reproduce it (as minimally and precisely as possible):**
**Anything else we need to know?:**
**Environment**
- Platform(OS/Version):
- Host Node Version:
- Target Node Version:
- Nexe version:
- Python Version:
-108
View File
@@ -1,108 +0,0 @@
/**
* Copyright (c) 2013 Craig Condon
* Copyright (c) 2015-2016 Jared Allard
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**/
var mdeps = require("module-deps"),
path = require("path"),
spawn = require('child_process').spawn,
fs = require("fs"),
async = require("async"),
browserify = require('browserify'),
builtins = require("builtins"),
_log = require("./log");
/**
* User browserify to create a "packed" file.
*
* @param {String} input - input file
* @param {String} nc - node compiler dir
* @param {Function} complete - next function to call (async)
**/
function bundle (input, nc, complete) {
var bundlePath = path.join(nc, "lib", "nexe.js");
var ws = fs.createWriteStream(bundlePath);
var browserifyExec;
var browserifyLocations = [ // TODO: Do this programattically.
'This is to ignore the 0 location.',
path.join(__dirname, '../', 'node_modules/browserify/bin/cmd.js'),
path.join(__dirname, '../../', 'node_modules/browserify/bin/cmd.js'),
path.join(__dirname, '../../../', 'node_modules/browserify/bin/cmd.js'),
path.join(__dirname, '../../../../', 'node_modules/browserify/bin/cmd.js')
]
for(var i = 0; i !== (browserifyLocations.length-1); i++) {
if(fs.existsSync(browserifyLocations[i])) {
browserifyExec = browserifyLocations[i];
_log('found browserify in', browserifyExec);
break;
}
}
var proc = spawn('node', [browserifyExec, '--node', input ]);
proc.stdout.pipe(ws);
proc.on('error', function(err) {
console.error(err.toString('ascii'));
_log('error', 'Failed to invoke browserify');
process.exit(1);
})
proc.stderr.on('data', function(data) {
console.error(data.toString('ascii'));
if(!fs.existsSync(input)) {
_log('error', 'Your input file doesn\'t exist!');
}
_log('error', 'Browserify failed to launch');
process.exit(1);
});
proc.on('close', function(code) {
_log("Browserify finished");
})
ws.on('error', function(err) {
console.log(err);
_log('error', 'Failed to save stdout to disk');
process.exit(1);
})
ws.on('close', function() {
var source = fs.readFileSync(bundlePath, 'utf8');
source = source.replace(/[^\x00-\x7F]/g, "");
// write the source modified to nexe.js
fs.writeFile(bundlePath, source, 'utf8', function(err) {
if(err) {
_log('error', 'failed to save source');
process.exit(1);
}
complete();
});
});
}
module.exports = bundle;
-65
View File
@@ -1,65 +0,0 @@
/**
* Copyright (c) 2013 Craig Condon
* Copyright (c) 2015-2016 Jared Allard
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**/
var path = require("path"),
fs = require("fs");
/**
* TODO: Optimize this code. (more like document it so I understand it better.)
**/
function embed(resourceFiles, resourceRoot, complete) {
function encode(filePath) {
return fs.readFileSync(filePath).toString('base64');
}
resourceFiles = resourceFiles || [];
resourceRoot = resourceRoot || "";
if (!Array.isArray(resourceFiles)) {
throw new Error("Bad Argument: resourceFiles is not an array");
}
var buffer = "var embeddedFiles = {\n";
for (var i = 0; i < resourceFiles.length; ++i) {
buffer += JSON.stringify(path.relative(resourceRoot, resourceFiles[i])) + ': "';
buffer += encode(resourceFiles[i]) + '",\n';
}
buffer += "\n};\n\nmodule.exports.keys = function () { return Object.keys(embeddedFiles); }\n\nmodule.exports.get = ";
buffer += accessor.toString();
complete(null, buffer);
}
var accessor = function (key) {
if (embeddedFiles.hasOwnProperty(key)) {
return new Buffer(embeddedFiles[key], 'base64');
}
else {
//file was not embedded, throw err.
throw new Error('Embedded file not found');
}
}
module.exports = embed;
-879
View File
@@ -1,879 +0,0 @@
/**
* Copyright (c) 2013 Craig Condon
* Copyright (c) 2015-2016 Jared Allard
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**/
var async = require("async"),
outcome = require("outcome"),
mkdirp = require("mkdirp"),
request = require("request"),
path = require("path"),
fs = require("fs"),
colors = require('colors'),
ncp = require("ncp").ncp,
ProgressBar = require("progress"),
child_process = require("child_process"),
glob = require("glob"),
bundle = require("./bundle"),
embed = require("./embed"),
os = require("os"),
targz = require('tar.gz'),
_log = require("./log"),
_monkeypatch = require("./monkeypatch"),
spawn = child_process.spawn;
var isWin = /^win/.test(process.platform);
var isPy;
var framework;
var version;
var option_list = [
"python",
"input",
"output",
"nodeTempDir",
"flags",
"framework",
"nodeConfigureArgs",
"nodeMakeArgs",
"nodeVersion"
]
/**
* Compiliation process.
*/
exports.compile = function (options, complete) {
var nodeCompiler, nexeEntryPath;
async.waterfall([
/**
*check relevant options
*/
function checkOpts (next) {
/* failsafe */
if(options === undefined) {
_log("error", "no options given to .compile()");
process.exit()
}
/* option_list.forEach(function(v) {
if(options[v] === undefined) {
_log("error", "option "+v+" was empty")
process.exit();
}
}); */
/**
* Have we been given a custom flag for python executable?
**/
if(options.python!=='python' && options.python!==""
&& options.python!==undefined) {
if(isWin) {
isPy=options.python.replace(/\//gm, "\\"); // use windows file paths, batch is sensitive.
} else {
isPy=options.python;
}
_log("set python as "+isPy);
} else {
isPy="python";
}
// remove dots
options.framework = options.framework.replace(/\./g, "");
// set outter-scope framework variable.
framework = options.framework;
_log("framework => "+framework);
version = options.nodeVersion; // better framework vc
// check iojs version
if (framework === "iojs" && version === "latest" ) {
_log("fetching iojs versions");
mkdirp(options.nodeTempDir); // make temp dir, probably repetive.
// create write stream so we have control over events
var output = fs.createWriteStream(path.join(options.nodeTempDir,
"iojs-versions.json"));
request.get("https://iojs.org/dist/index.json")
.pipe(output);
output.on('close', function() {
_log("done");
var f = fs.readFileSync(path.join(options.nodeTempDir,
"iojs-versions.json"));
f = JSON.parse(f);
version = f[0].version.replace("v", "");
_log("iojs latest => "+version);
// continue down along the async road
next();
});
} else {
next();
}
},
/**
* first download node
*/
function downloadNode (next) {
_downloadNode(version, options.nodeTempDir, options.nodeConfigureArgs, options.nodeMakeArgs, next);
},
/**
* bundle the application into one script
*/
function combineProject (nc, next) {
nodeCompiler = nc;
_log("bundle %s", options.input);
bundle(options.input, nodeCompiler.dir, next);
},
function embedResources(next) {
_log("embedResources %s", options.resourceFiles);
embed(options.resourceFiles, options.resourceRoot, next);
},
function writeResources (resources, next) {
resourcePath = path.join(nodeCompiler.dir, "lib", "nexeres.js");
_log("resource -> %s", resourcePath);
fs.writeFile(resourcePath, resources, next);
},
/**
* monkeypatch some files so that the nexe.js file is loaded when the app runs
*/
function monkeyPatchNodeConfig (next) {
_monkeyPatchNodeConfig(nodeCompiler, next);
},
/**
* monkeypatch node.cc to prevent v8 and node from processing CLI flags
*/
function monkeyPatchNodeCc (next) {
if (options.flags) {
_monkeyPatchMainCc(nodeCompiler, next);
} else {
next();
}
},
function monkeyPatchv8FlagsCc (next) {
if(options.jsFlags) {
return _monkeyPatchv8FlagsCc(nodeCompiler, options, next);
}
return next();
},
/**
* monkeypatch child_process.js so nexejs knows when it is a forked process
*/
function monkeyPatchChildProc(next) {
_monkeyPatchChildProcess(nodeCompiler, next);
},
/**
* If an old compiled executable exists in the Release directory, delete it.
* This lets us see if the build failed by checking the existence of this file later.
*/
function cleanUpOldExecutable (next) {
fs.unlink(nodeCompiler.releasePath, function (err) {
if (err) {
if (err.code === "ENOENT") {
next();
} else {
throw err;
}
} else {
next();
}
});
},
/**
* compile the node application
*/
function makeExecutable (next) {
if(isWin) {
_log("vcbuild [make stage]");
} else {
_log("make");
}
nodeCompiler.make(next);
},
/**
* we create the output directory if needed
*/
function makeOutputDirectory (next) {
mkdirp(path.dirname(options.output), function(){ next(); });
},
/**
* Verify that the executable was compiled successfully
*/
function checkThatExecutableExists (next) {
fs.exists(nodeCompiler.releasePath, function (exists) {
if (!exists) {
_log("error",
"The release executable has not been generated. " +
"This indicates a failure in the build process. " +
"There is likely additional information above."
);
process.exit(1);
} else {
next();
}
});
},
/**
* Copy the compilied binary to the output specified.
*/
function copyBinaryToOutput (next) {
_log("cp %s %s", nodeCompiler.releasePath, options.output);
ncp(nodeCompiler.releasePath, options.output, function (err) {
if (err) {
_log("error", "Couldn't copy binary.");
throw err; // dump raw error object
}
_log('copied');
next();
});
}
], complete);
}
/**
* Download a version of node
*
* @param {string} version, version of node to download
* @param {string} directory, where to store the downloaded src
* @param {function} complete, callback
*/
function _downloadNode(version, directory, nodeConfigureArgs, nodeMakeArgs, complete) {
var nodeFileDir = path.resolve(path.join(directory, framework, version)), // fixes #107, was process.cwd(), + rest.
nodeFilePath = path.resolve(path.join(nodeFileDir, framework + "-" + version + ".tar.gz"));
// might already be downloaded, and unzipped
if (_getNodeCompiler(nodeFileDir, nodeConfigureArgs, nodeMakeArgs, complete)) {
return;
}
async.waterfall([
/**
* first make the directory where the zip file will live
*/
function makeDirectory (next) {
mkdirp.sync(path.dirname(nodeFilePath));
next();
},
/**
* download node into the target directory
*/
function downloadNode (next) {
if (fs.existsSync(nodeFilePath)) return next();
var uri = framework;
if(framework==="node") {
uri = 'nodejs'; // if node, use nodejs uri
} else if(framework === 'nodejs') {
framework = 'node'; // support nodejs, and node, as framework.
}
var type = global.type;
var url, prefix = "https://"+uri+".org/dist";
if (version === "latest") {
url = prefix + "/" + framework + "-" + version + ".tar.gz";
} else {
url = prefix + "/v" + version + "/"+framework+"-v" + version + ".tar.gz";
}
_log("downloading %s", url);
var output = fs.createWriteStream(nodeFilePath, { "flags": "w+" });
// need to set user-agent to bypass some corporate firewalls
var requestOptions = {
url: url,
headers: {
"User-Agent": "Node.js"
}
}
_logProgress(request(requestOptions)).pipe(output);
output.on("close", function () { next(); });
},
/**
* unzip in the same directory
*/
function unzipNodeTarball (next) {
if(isWin) {
_log("Extracting the .tar.gz.");
new targz().extract(nodeFilePath, nodeFileDir, next);
} else {
var cmd = ["tar", "-xf", nodeFilePath, "-C", nodeFileDir];
_log(cmd.join(" "));
var tar = spawn(cmd.shift(), cmd);
tar.stdout.pipe(process.stdout);
tar.stderr.pipe(process.stderr);
tar.on("close", function () { next(); })
tar.on("error", function (err) {
console.log(err);
_log("error", "failed to extract the node tar file.");
process.exit(1);
});
}
},
/**
* return the compiler object for the node version
*/
function (next, type) {
_getNodeCompiler(nodeFileDir, nodeConfigureArgs, nodeMakeArgs, next, type)
},
], complete);
}
/**
* Get the compilier we will use for whatever platform we may be on and configure
* it.
*/
function _getNodeCompiler (nodeFileDir, nodeConfigureArgs, nodeMakeArgs, complete, type) {
var dir = _getFirstDirectory(nodeFileDir);
// standard
var executable = "node.exe";
var binary = "node";
// iojs specifics.
if(framework === "iojs") {
executable = "iojs.exe";
binary = "iojs";
}
if (dir) {
if(isWin) {
complete(null, {
dir: dir,
version: path.basename(nodeFileDir),
releasePath: path.join(dir, "Release", executable),
make: function(next) {
// create a new env with minimal impact on old one
var newEnv = process.env
if(isPy!=="python") {
// add the dir of the suposed python exe to path
newEnv.path = process.env.PATH+";"+path.dirname(isPy)
}
// spawn a vcbuild process with our custom enviroment.
var vcbuild = spawn("vcbuild.bat", ["nosign", "release"], {
cwd: dir,
env: newEnv
});
vcbuild.stdout.pipe(process.stdout);
vcbuild.stderr.pipe(process.stderr);
vcbuild.on("close", function() {
next();
});
}
});
} else {
complete(null, {
dir: dir,
version: path.basename(nodeFileDir),
releasePath: path.join(dir, "out", "Release", binary),
make: function (next) {
var cfg = "./configure",
configure;
var conf = [cfg];
if(isPy !== "python") {
conf = [conf].concat(nodeConfigureArgs);
}
// should work for all use cases now.
configure = spawn(isPy, conf, {
cwd: dir.toString('ascii')
});
// local function, move to top eventually
function _loop(dir) {
/* eventually try every python file */
var pdir = fs.readdirSync(dir);
pdir.forEach(function(v, i) {
var stat = fs.statSync(dir+"/"+v);
if(stat.isFile()) {
// only process Makefiles and .mk targets.
if(v !== "Makefile" && path.extname(v) !== ".mk") {
return;
}
_log("patching "+v);
/* patch the file */
var py = fs.readFileSync(dir+"/"+v, {encoding: 'utf8'});
py = py.replace(/([a-z]|\/)*python(\w|)/gm, isPy); // this is definently needed
fs.writeFileSync(dir+"/"+v, py, {encoding: 'utf8'}); // write to file
delete pv;
} else if(stat.isDirectory()) {
// must be dir?
// skip tests because we don't need them here
if(v !== "test") {
_loop(dir+"/"+v)
}
}
});
}
configure.stdout.pipe(process.stdout);
configure.stderr.pipe(process.stderr);
// on error
configure.on("error", function(err) {
console.log('Error:', err);
console.log('');
console.log('Details:');
console.log('command =', isPy, cfg, conf_args || '');
console.log('cwd =', dir);
var configure_path = path.join(dir, 'configure');
var contains_configure = fs.existsSync(configure_path);
console.log('cwd contains configure,', (contains_configure ? colors.green('yes') : colors.red('no')));
var configure_size = fs.statSync(configure_path).size;
console.log('configure is non-zero size,', ((configure_size > 0 ) ? colors.green('yes') : colors.red('no')));
_log("error", "failed to launch configure.");
process.exit(1);
});
// when it's finished
configure.on("close", function () {
if(isPy !== "python") {
/**
* Originally I thought this only applied to io.js,
* however I soon found out this affects node.js,
* so it is now mainstream.
*/
_log("preparing python");
// loop over depends
_loop(dir);
}
if(nodeMakeArgs === undefined) {
nodeMakeArgs = [];
}
var platformMake = "make";
if (os.platform().match(/bsd$/) != null) {
platformMake = "gmake";
}
var make = spawn(platformMake, nodeMakeArgs, { cwd: dir });
make.stdout.pipe(process.stdout);
make.stderr.pipe(process.stderr);
make.on("error", function(err) {
console.log(err);
_log("error", "failed to run make.");
process.exit(1);
})
make.on("close", function () {
next();
});
})
}
});
}
return true;
}
return false;
}
/**
*/
function _monkeyPatchNodeConfig (compiler, complete) {
async.waterfall([
/**
* monkeypatch the gyp file to include the nexe.js and nexeres.js files
*/
function (next) {
_monkeyPatchGyp(compiler, next)
},
/**
* monkeypatch main entry point
*/
function (next) {
_monkeyPatchMainJs(compiler, next)
}
], complete);
}
/**
* patch the gyp file to allow our custom includes
*/
function _monkeyPatchGyp (compiler, complete) {
var gypPath = path.join(compiler.dir, "node.gyp");
_monkeypatch(
gypPath,
function (content) {
return ~content.indexOf("nexe.js");
},
function (content, next) {
next(null, content.replace("'lib/fs.js',", "'lib/fs.js', 'lib/nexe.js', 'lib/nexeres.js', "))
},
complete
)
}
/**
*/
function _monkeyPatchMainJs (compiler, complete) {
var mainPath = path.join(compiler.dir, "src", "node.js");
_monkeypatch(
mainPath,
function (content) {
return ~content.indexOf("nexe");
},
function (content, next) {
next(null, content.replace(/\(function\(process\) \{/,'\
(function(process) {\n\
process._eval = \'require("nexe");\';\n\
if (process.argv[1] !== "nexe.js") {\n\
process.argv.splice(1, 0, "nexe.js");\n\
}\n\
'))
},
complete
);
}
/**
*/
function _monkeyPatchChildProcess(compiler, complete) {
var childProcPath = path.join(compiler.dir, "lib", "child_process.js");
_monkeypatch(
childProcPath,
function (content) {
return ~content.indexOf("--child_process");
},
function (content, next) {
next(null, content.replace(/return spawn\(/, 'args.unshift("nexe.js", "--child_process");\n return spawn('));
},
complete
);
}
/**
* Patch node.cc to not check the internal arguments.
*/
function _monkeyPatchMainCc(compiler, complete) {
var mainPath = path.join(compiler.dir, "src", "node.cc");
_monkeypatch(
mainPath,
function (content) {
return ~content.indexOf('// // TODO use parse opts');
},
function (content, next) {
var lines = content.split('\n');
var endLine = lines.indexOf(' option_end_index = i;'); // pre node 0.11.6 compat
if(endLine !== -1) { // check if it succedded or not.
lines[endLine] = ' option_end_index = 1;';
_log("patched node.cc")
}
/**
* This is the new method of passing the args. Tested on node.js 0.12.5
* and iojs 2.3.1
**/
if(endLine === -1) { // only if the pre-0.12.5 failed.
var startLine = lines.indexOf(' while (index < nargs && argv[index][0] == \'-\') {'); // beginning of the function
endLine = lines.indexOf(' // Copy remaining arguments.');
endLine = endLine-1; // space, then it's at the }
// remove the offending lines
if(startLine !== -1) {
for (var i = startLine; i < endLine; i++) {
lines[i] = undefined; // set the value to undefined so it's skipped by the join
}
_log('patched node.cc');
}
}
lines = lines.join('\n');
next(null, lines);
},
complete
);
}
/**
* Patch flags.cc from deps/v8/src to use hard-coded flags.
* this function is very closely ready to accept custom injection code.
**/
function _monkeyPatchv8FlagsCc(compiler, options, complete) {
var mainPath = path.join(compiler.dir, "deps/v8/src", "flags.cc");
fs.readFile(mainPath, {encoding: 'utf8'}, function(err, contents) {
if(err) {
return _log('error', 'failed to read', mainPath);
}
// Super simple injection here. Perhaps make it an array at somepoint?
var injection = '\
const char* nexevargs = "{{args}}";\n\
int nexevargslen = strlen(nexevargs);\n\
SetFlagsFromString(nexevargs, nexevargslen);\n\
';
var injectionSplit = injection.split('\n');
var injectionLength = injectionSplit.length;
var contentsSplit = contents.split('\n');
var contentsLength = contentsSplit.length;
var lastInjectionLine = injectionSplit[injectionLength-2];
var lineToInjectAfter = contentsSplit.indexOf(' ComputeFlagListHash();');
var haveWeInjectedBefore = contentsSplit.indexOf(lastInjectionLine);
var lineInjectDifference = contentsLength - lineToInjectAfter;
// support for 0.12.x
if(lineToInjectAfter===-1) {
_log('warn', 'Using an expiramental support patch for 0.12.x');
lineToInjectAfter = contentsSplit.indexOf('#undef FLAG_MODE_DEFINE_IMPLICATIONS');
}
// this is debug, comment out.
// _log('v8 injection is', injectionLength, 'newlines long');
// _log('v8 flags source is', contentsLength, 'newlines long');
// console.log(finalContents)
var finalContents,
dontCombine;
if(lineToInjectAfter !== -1 && haveWeInjectedBefore === -1) {
_log('injecting v8 args c++');
// super debug
// _log('v8 injection determined by', lastInjectionLine);
// _log('v8 inject after line', lineToInjectAfter);
// _log('v8 inject needs to shift', lineInjectDifference, 'amount of lines by', injectionLength);
// compute out the amount of space we'll need in this.
var startShiftLine = contentsLength-1; // minus one to make up for 0 arg line.
var endShiftLine = lineToInjectAfter;
var injectRoom = injectionLength-1;
injectionSplit[0] = injectionSplit[0].replace('{{args}}', options.jsFlags);
for (var i = startShiftLine; i !== endShiftLine; i--) {
contentsSplit[i+injectRoom] = contentsSplit[i];
contentsSplit[i] = '';
}
var injectionPos = 0;
for(var i = 0; i !== injectionLength-1; i++) {
contentsSplit[(lineToInjectAfter+1)+injectionPos] = injectionSplit[injectionPos];
injectionPos++;
}
} else if(lineToInjectAfter !== -1 && haveWeInjectedBefore !== -1) {
_log('re-injecting v8 args');
dontCombine = true;
finalContents = contentsSplit.join('\n');
finalContents = finalContents.replace(/const char\* nexevargs = "[A-Z\-\_]*";/gi,
'const char* nexevargs = "'+options.jsFlags+'";');
} else {
_log('warn', 'failed to find a suitable injection point for v8 args.',
'File a bug report with the node version and log.');
_log('lineToInjectAfter='+lineToInjectAfter, 'haveWeInjectedBefore='+haveWeInjectedBefore);
return process.exit(1);
}
if(!dontCombine) {
finalContents = contentsSplit.join('\n');
}
fs.writeFile(mainPath, finalContents, {encoding: 'utf8'}, function(err) {
if(err) {
_log('error', 'failed to write to', mainPath);
return process.exit(1);
}
return complete();
})
});
}
/**
* Get the first directory of a string.
*/
function _getFirstDirectory (dir) {
var files = glob.sync(dir + "/*");
for (var i = files.length; i--;) {
var file = files[i];
if (fs.statSync(file).isDirectory()) return file;
}
return false;
}
/**
* Log the progress of a request object.
*/
function _logProgress (req) {
req.on("response", function (resp) {
var len = parseInt(resp.headers["content-length"], 10),
bar = new ProgressBar("[:bar]", {
complete: "=",
incomplete: " ",
total: len,
width: 100 // just use 100
});
req.on("data", function (chunk) {
bar.tick(chunk.length);
});
});
req.on("error", function(err) {
console.log(err);
_log("error", "failed to download node sources,");
process.exit(1);
});
return req;
}
/**
* Attempt to parse the package.json for nexe information.
*
* @param {string} path - path to package.json
* @param {object} options - fallback options
*
* @todo implement options overriding package defaults.
* @todo make this much less hackily implemented....
*
* @return {object} nexe.compile options object
**/
exports.package = function(path, options) {
var _package; // scope
// check if the file exists
if(fs.existsSync(path)===false) {
_log("warn", "no package.json found.");
} else {
_package = require(path);
}
// replace ^$ w/ os specific extension on output
if(isWin) {
_package.nexe.output = _package.nexe.output.replace(/\^\$/, '.exe') // exe
} else {
_package.nexe.output = _package.nexe.output.replace(/\^\$/, '') // none
}
// construct the object
var obj = {
input: (_package.nexe.input || options.i),
output: (_package.nexe.output || options.o),
flags: (_package.nexe.runtime.ignoreFlags || (options.f || false)),
nodeMakeArgs: (_package.nexe.runtime.nodeMakeArgs || []),
resourceFiles: (_package.nexe.resourceFiles),
nodeVersion: (_package.nexe.runtime.version || options.r),
nodeConfigureArgs: (_package.nexe.runtime.nodeConfigureArgs || []),
nodeMakeArgs: (_package.nexe.runtime.nodeMakeArgs || []),
jsFlags: (_package.nexe.runtime['js-flags'] || false),
python: (_package.nexe.python || options.p),
nodeTempDir: (_package.nexe.temp || options.t),
framework: (_package.nexe.runtime.framework || options.f)
}
Object.keys(_package.nexe).forEach(function(v,i) {
if(v!=="runtime") {
_log("log", v+" => '"+_package.nexe[v]+"'");
}
});
Object.keys(_package.nexe.runtime).forEach(function(v,i) {
_log("log", "runtime."+v+" => '"+_package.nexe.runtime[v]+"'");
});
return obj;
}
-1
View File
@@ -1 +0,0 @@
module.exports = require("./exe");
-57
View File
@@ -1,57 +0,0 @@
/**
* Copyright (c) 2013 Craig Condon
* Copyright (c) 2015-2016 Jared Allard
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**/
var colors = require('colors');
/**
* standard output, takes 3 different types.
* log, error, and warn
*
* @param {any} arguments - Text to output.
* @return undefined
**/
function _log () {
var args = Array.prototype.slice.call(arguments, 0),
level = args.shift();
if (!~["log", "error", "warn"].indexOf(level)) {
args.unshift(level);
level = "log";
}
if(level == "log") {
args[0] = "----> " + args[0];
} else if(level == "error") {
args[0] = "....> " + colors.red("ERROR: ") + args[0]
} else if(level == "warn") {
args[0] = "....> " + colors.yellow("WARNING: ") + args[0]
}
console[level].apply(console, args);
}
// export the log function, for require()
module.exports = _log;
-63
View File
@@ -1,63 +0,0 @@
/**
* Copyright (c) 2013 Craig Condon
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**/
var async = require("async"),
fs = require("fs"),
_log = require("./log");
/**
* Monkey patch a file.
*
* @param {string} filePath - path to file.
* @param {function} monkeyPatched - function to process contents
* @param {function} processor - TODO: detail what this is
* @param {function} complete - callback
*
* @return undefined
*/
function _monkeypatch (filePath, monkeyPatched, processor, complete) {
async.waterfall([
function read (next) {
fs.readFile(filePath, "utf8", next);
},
// TODO - need to parse gyp file - this is a bit hacker
function monkeypatch (content, next) {
if (monkeyPatched(content)) return complete();
_log("monkey patch %s", filePath);
processor(content, next);
},
function write (content, next) {
fs.writeFile(filePath, content, "utf8", next);
}
], complete);
}
// export the function for require()
module.exports = _monkeypatch;
+3523
View File
File diff suppressed because it is too large Load Diff
+63 -41
View File
@@ -1,54 +1,76 @@
{ {
"author": "Jared Allard <jaredallard@outlook.com>",
"name": "nexe", "name": "nexe",
"description": "create single executables out of your [node/io].js applications", "description": "Create a single executable out of your Node.js application",
"license": "MIT", "license": "MIT",
"version": "1.0.7", "version": "3.3.2",
"contributors": [ "contributors": [
{ "Craig Condon <craig.j.condon@gmail.com> (http://crcn.io)",
"name": "Criag Condon", "Jared Allard <jaredallard@outlook.com>",
"email": "craig.j.condon@gmail.com", "Caleb Boyd <caleb.boyd@hotmail.com>"
"url": "http://crcn.io/"
}
], ],
"scripts": {
"ci:build": "ts-node tasks/build",
"test": "mocha",
"lint": "tslint \"{src,plugins,tasks}/**/*.ts\" --fix",
"prepare": "npm run lint && npm run build && npm test",
"prebuild": "rimraf lib",
"build": "tsc --declaration",
"postbuild": "ts-node tasks/post-build"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/jaredallard/nexe.git" "url": "git://github.com/nexe/nexe.git"
}, },
"main": "./lib/index.js", "files": [
"dependencies": { "lib"
"async": "1.5.2", ],
"browserify": "^13.0.0", "typings": "lib/nexe.d.ts",
"builtins": "1.0.3", "main": "index.js",
"colors": "^1.1.2",
"glob": "^6.0.4",
"insert-module-globals": "^7.0.1",
"mkdirp": "^0.5.1",
"module-deps": "^4.0.5",
"ncp": "^2.0.0",
"outcome": "0.0.18",
"progress": "^1.1.8",
"request": "^2.67.0",
"sprintf": "~0.1.5",
"step": "0.0.x",
"tar.gz": "1.0.2",
"yargs": "^3.32.0"
},
"nexe": {
"input": "./bin/nexe",
"output": "nexe^$",
"temp": "src",
"runtime": {
"framework": "nodejs",
"version": "5.5.0",
"ignoreFlags": true
}
},
"preferGlobal": true,
"bin": { "bin": {
"nexe": "./bin/nexe" "nexe": "index.js"
},
"engines": {
"node": ">=6"
},
"dependencies": {
"@calebboyd/semaphore": "^1.3.1",
"app-builder": "^5.2.0",
"caw": "^2.0.1",
"chalk": "^2.4.2",
"cherow": "1.6.9",
"download": "^7.1.0",
"globby": "^9.2.0",
"got": "^9.6.0",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"multistream": "^2.1.1",
"ora": "^3.4.0",
"pify": "^4.0.1",
"resolve-dependencies": "^2.2.3",
"rimraf": "^2.6.3"
}, },
"devDependencies": { "devDependencies": {
"nodeunit": "^0.9.1" "@types/chai": "^4.1.7",
"@types/download": "^6.2.4",
"@types/execa": "^0.9.0",
"@types/globby": "^9.1.0",
"@types/got": "^9.6.0",
"@types/minimist": "^1.2.0",
"@types/mkdirp": "^0.5.2",
"@types/mocha": "^5.2.7",
"@types/multistream": "^2.1.1",
"@types/ora": "^3.2.0",
"@types/pify": "3.0.2",
"@types/rimraf": "2.0.2",
"@types/semver": "^6.0.0",
"chai": "^4.2.0",
"execa": "^1.0.0",
"mocha": "^6.1.4",
"prettier": "^1.18.2",
"ts-node": "^8.3.0",
"tslint": "^5.17.0",
"tslint-config-prettier": "^1.18.0",
"tslint-plugin-prettier": "^2.0.1",
"typescript": "^3.5.2"
} }
} }
+319
View File
@@ -0,0 +1,319 @@
import { delimiter, resolve, normalize, join } from 'path'
import { Buffer } from 'buffer'
import { createReadStream, ReadStream } from 'fs'
import { spawn } from 'child_process'
import { Logger, LogStep } from './logger'
import {
readFileAsync,
writeFileAsync,
pathExistsAsync,
statAsync,
dequote,
isWindows,
bound,
semverGt
} from './util'
import { NexeOptions, version } from './options'
import { NexeTarget } from './target'
import combineStreams = require('multistream')
import { Bundle, toStream } from './fs/bundle'
const isBsd = Boolean(~process.platform.indexOf('bsd'))
const make = isWindows ? 'vcbuild.bat' : isBsd ? 'gmake' : 'make'
const configure = isWindows ? 'configure' : './configure'
type StringReplacer = (match: string) => string
export interface NexeFile {
filename: string
absPath: string
contents: string
}
export { NexeOptions }
export class NexeError extends Error {
constructor(m: string) {
super(m)
Object.setPrototypeOf(this, NexeError.prototype)
}
}
export class NexeCompiler {
/**
* Epoch of when compilation started
*/
private start = Date.now()
private compileStep: LogStep | undefined
public log = new Logger(this.options.loglevel)
/**
* Copy of process.env
*/
public env = { ...process.env }
/**
* Virtual FileSystem
*/
public bundle: Bundle
/**
* Root directory for the source of the current build
*/
public src: string
/**
* In memory files that are being manipulated by the compiler
*/
public files: NexeFile[] = []
/**
* Standalone pieces of code run before the application entrypoint
*/
public shims: string[] = []
/**
* The last shim (defaults to "require('module').runMain()")
*/
public startup: string = ''
/**
* The main entrypoint filename for your application - eg. node mainFile.js
*/
public entrypoint: string | undefined
/**
* Not used
*/
public targets: NexeTarget[]
/**
* Current target of the compiler
*/
public target: NexeTarget
/**
* Output filename (-o myapp.exe)
*/
public output = this.options.output
/**
* Flag to indicate whether or notstdin was used for input
*/
public stdinUsed = false
/**
* Path to the configure script
*/
public configureScript: string
/**
* The file path of node binary
*/
public nodeSrcBinPath: string
/**
* Remote asset path if available
*/
public remoteAsset: string
constructor(public options: NexeOptions) {
const { python } = (this.options = options)
//SOMEDAY iterate over multiple targets with `--outDir`
this.targets = options.targets as NexeTarget[]
this.target = this.targets[0]
if (options.asset && options.asset.startsWith('http')) {
this.remoteAsset = options.asset
} else {
this.remoteAsset =
'https://github.com/nexe/nexe/releases/download/v3.0.0/' + this.target.toString()
}
this.src = join(this.options.temp, this.target.version)
this.configureScript = configure + (semverGt(this.target.version, '10.10.0') ? '.py' : '')
this.nodeSrcBinPath = isWindows
? join(this.src, 'Release', 'node.exe')
: join(this.src, 'out', 'Release', 'node')
this.log.step('nexe ' + version, 'info')
this.bundle = new Bundle(options)
if (isWindows) {
const originalPath = process.env.PATH
delete process.env.PATH
this.env = { ...process.env }
this.env.PATH = python
? (this.env.PATH = dequote(normalize(python)) + delimiter + originalPath)
: originalPath
process.env.PATH = originalPath
} else {
this.env = { ...process.env }
python && (this.env.PYTHON = python)
}
}
@bound
addResource(file: string, content?: Buffer | string) {
return this.bundle.addResource(file, content)
}
get binaryConfiguration() {
return { resources: this.bundle.index }
}
get resourceSize() {
return this.bundle.blobSize
}
@bound
async readFileAsync(file: string) {
this.assertBuild()
let cachedFile = this.files.find(x => normalize(x.filename) === normalize(file))
if (!cachedFile) {
const absPath = join(this.src, file)
cachedFile = {
absPath,
filename: file,
contents: await readFileAsync(absPath, 'utf-8').catch(x => {
if (x.code === 'ENOENT') return ''
throw x
})
}
this.files.push(cachedFile)
}
return cachedFile
}
@bound
writeFileAsync(file: string, contents: string | Buffer) {
this.assertBuild()
return writeFileAsync(join(this.src, file), contents)
}
@bound
async replaceInFileAsync(file: string, replace: string | RegExp, value: string | StringReplacer) {
const entry = await this.readFileAsync(file)
entry.contents = entry.contents.replace(replace, value as any)
}
@bound
async setFileContentsAsync(file: string, contents: string) {
const entry = await this.readFileAsync(file)
entry.contents = contents
}
quit(error?: any) {
const time = Date.now() - this.start
this.log.write(`Finished in ${time / 1000}s`, error ? 'red' : 'green')
return this.log.flush()
}
assertBuild() {
if (!this.options.build) {
throw new NexeError('This feature is only available with `--build`')
}
}
public getNodeExecutableLocation(target?: NexeTarget) {
if (this.options.asset && !this.options.asset.startsWith('http')) {
return resolve(this.options.cwd, this.options.asset)
}
if (target) {
return join(this.options.temp, target.toString())
}
return this.nodeSrcBinPath
}
private _runBuildCommandAsync(command: string, args: string[]) {
if (this.log.verbose) {
this.compileStep!.pause()
}
return new Promise((resolve, reject) => {
spawn(command, args, {
cwd: this.src,
env: this.env,
stdio: this.log.verbose ? 'inherit' : 'ignore'
})
.once('error', (e: Error) => {
if (this.log.verbose) {
this.compileStep!.resume()
}
reject(e)
})
.once('close', (code: number) => {
if (this.log.verbose) {
this.compileStep!.resume()
}
if (code != 0) {
const error = `${command} ${args.join(' ')} exited with code: ${code}`
reject(new NexeError(error))
}
resolve()
})
})
}
private _configureAsync() {
if (isWindows && semverGt(this.target.version, '10.15.99')) {
return Promise.resolve()
}
return this._runBuildCommandAsync(this.env.PYTHON || 'python', [
this.configureScript,
...this.options.configure
])
}
public async build(): Promise<ReadStream> {
this.compileStep!.log(
`Configuring node build${
this.options.configure.length ? ': ' + this.options.configure : '...'
}`
)
await this._configureAsync()
const buildOptions = this.options.make
this.compileStep!.log(
`Compiling Node${buildOptions.length ? ' with arguments: ' + buildOptions : '...'}`
)
await this._runBuildCommandAsync(make, buildOptions)
return createReadStream(this.getNodeExecutableLocation())
}
private async _shouldCompileBinaryAsync(
binary: NodeJS.ReadableStream | null,
location: string | undefined
) {
//SOMEDAY combine make/configure/vcBuild/and modified times of included files
const { snapshot, build } = this.options
if (!binary) {
return true
}
if (build && snapshot != null && (await pathExistsAsync(snapshot))) {
const snapshotLastModified = (await statAsync(snapshot)).mtimeMs
const binaryLastModified = (await statAsync(location)).mtimeMs
return snapshotLastModified > binaryLastModified
}
return false
}
async compileAsync(target: NexeTarget) {
const step = (this.compileStep = this.log.step('Compiling result'))
const build = this.options.build
const location = this.getNodeExecutableLocation(build ? undefined : target)
let binary = (await pathExistsAsync(location)) ? createReadStream(location) : null
if (await this._shouldCompileBinaryAsync(binary, location)) {
binary = await this.build()
step.log('Node binary compiled')
}
return this._assembleDeliverable(binary!)
}
code() {
return [this.shims.join(''), this.startup].join(';')
}
private _assembleDeliverable(binary: NodeJS.ReadableStream) {
if (!this.options.mangle) {
return binary
}
const startup = this.code(),
codeSize = Buffer.byteLength(startup)
const lengths = Buffer.from(Array(16))
lengths.writeDoubleLE(codeSize, 0)
lengths.writeDoubleLE(this.bundle.blobSize, 8)
return combineStreams([
binary,
toStream(startup),
this.bundle.toStream(),
toStream(Buffer.concat([Buffer.from('<nexe~~sentinel>'), lengths]))
])
}
}
+40
View File
@@ -0,0 +1,40 @@
# nexe-fs
### Getting Started:
This module contains a set of patches used to create (and use) the Nexe Virtual FileSystem
In order for NVFS to work with node's `require` method. A custom build of node must be used. If using the `nexe` cli this will be handled automatically
The customization should be applied as early as possbile when node is starting up.
i.e. The contents of `nexe-fs/bootstrap` should be put [here](https://github.com/nodejs/node/blob/0827c80920311fa9d1e6989c8a73aaaeca962eb7/lib/internal/bootstrap/node.js#L27-L28).
### Creating a NVFS:
To create a virtual file system use the `Bundle` Object, in your build process:
```javascript
const { Bundle } = require('nexe-fs')
const bundle = new Bundle()
await bundle.addResource(absoluteFileName)
bundle.toStream().pipe(fs.createWriteStream('./myFsBlob'))
```
In the entrypoint of your application, the fs patch needs to be applied (executed as early as possible)
For example, In the build process, the application's entrypoint could be prepended with the following:
```javascript
const codeToPrependToEntrypoint = fs.readFileSync(require.resolve('nexe-fs/patch'), 'utf-8') + `
shimFs({
blobPath: 'location/of/myFsBlob',
resources: ${JSON.stringify(bundle)},
layout: {
resourceStart: 0 // the offset within the blob that is referenced by the bundle index
}
})`
```
Since a custom node build is being used. A great place for this code would be the node source file: `_third_party_main.js`
+20
View File
@@ -0,0 +1,20 @@
if (true) {
const __nexe_patches = (process.nexe = { patches: {} }).patches
const slice = [].slice
const __nexe_noop_patch = function (original) {
const args = slice.call(arguments, 1)
return original.apply(this, args)
}
const __nexe_patch = function (obj, method, patch) {
const original = obj[method]
if (!original) return
__nexe_patches[method] = patch
obj[method] = function() {
const args = [original].concat(slice.call(arguments))
return __nexe_patches[method].apply(this, args)
}
}
__nexe_patch((process).binding('fs'), 'internalModuleReadFile', __nexe_noop_patch)
__nexe_patch((process).binding('fs'), 'internalModuleReadJSON', __nexe_noop_patch)
__nexe_patch((process).binding('fs'), 'internalModuleStat', __nexe_noop_patch)
}
+70
View File
@@ -0,0 +1,70 @@
import { stat as getStat, Stats, createReadStream } from 'fs'
import { relative } from 'path'
import { Readable } from 'stream'
import combineStreams = require('multistream')
const stat = (file: string): Promise<Stats> => {
return new Promise((resolve, reject) => {
getStat(file, (err, stats) => (err ? reject(err) : resolve(stats)))
})
}
function makeRelative(cwd: string, path: string) {
return './' + relative(cwd, path)
}
export function toStream(content: Buffer | string) {
const readable = new Readable({ read() {} })
readable.push(content)
readable.push(null)
return readable
}
export type File = { absPath: string; contents: string; deps: FileMap }
export type FileMap = { [absPath: string]: File | null }
export interface BundleOptions {
entries: string[]
cwd: string
expand: boolean
loadContent: boolean
files: FileMap
}
export class Bundle {
constructor({ cwd }: { cwd: string } = { cwd: process.cwd() }) {
this.cwd = cwd
}
cwd: string
blobSize: number = 0
index: { [relativeFilePath: string]: [number, number] } = {}
streams: (Readable | (() => Readable))[] = []
async addResource(absoluteFileName: string, content?: Buffer | string) {
let length = 0
if (content !== undefined) {
length = Buffer.byteLength(content)
} else {
const stats = await stat(absoluteFileName)
length = stats.size
}
const start = this.blobSize
this.blobSize += length
this.index[makeRelative(this.cwd, absoluteFileName)] = [start, length]
this.streams.push(() => (content ? toStream(content) : createReadStream(absoluteFileName)))
}
concat() {
throw new Error('Not Implemented')
}
toStream() {
return combineStreams(this.streams)
}
toJSON() {
return this.index
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"name": "nexe-fs",
"version": "1.0.1",
"description": "Virtual File System used by nexe",
"main": "bundle.js",
"files": [
"*.d.ts",
"*.js"
],
"author": "Caleb Boyd",
"dependencies": {
"multistream": "^2.1.1"
},
"license": "MIT"
}
+374
View File
@@ -0,0 +1,374 @@
import { Stats } from 'fs'
export interface NexeBinary {
blobPath: string
resources: { [key: string]: number[] }
layout: {
stat: Stats
resourceStart: number
contentSize?: number
contentStart?: number
resourceSize?: number
}
}
let originalFsMethods: any = null
let lazyRestoreFs = () => {}
function shimFs(binary: NexeBinary, fs: any = require('fs')) {
if (originalFsMethods !== null) {
return
}
originalFsMethods = Object.assign({}, fs)
const { blobPath, resources: manifest } = binary,
{ resourceStart, stat } = binary.layout,
directories: { [key: string]: { [key: string]: boolean } } = {},
notAFile = '!@#$%^&*',
isWin = process.platform.startsWith('win'),
isString = (x: any): x is string => typeof x === 'string' || x instanceof String,
noop = () => {},
path = require('path'),
baseDir = path.dirname(process.execPath)
let log = (_: string) => true
if ((process.env.DEBUG || '').toLowerCase().includes('nexe:require')) {
log = (text: string) => process.stderr.write('[nexe] - ' + text + '\n')
}
const winPath = (key: string) => {
if (isWin && key.substr(1, 2) === ':\\') {
key = key[0].toUpperCase() + key.substr(1)
}
return key
}
const getKey = function getKey(filepath: string | Buffer | null): string {
if (Buffer.isBuffer(filepath)) {
filepath = filepath.toString()
}
if (!isString(filepath)) {
return notAFile
}
let key = path.resolve(baseDir, filepath)
return winPath(key)
}
const statTime = function() {
return {
dev: 0,
ino: 0,
nlink: 0,
rdev: 0,
uid: 123,
gid: 500,
blksize: 4096,
blocks: 0,
atime: new Date(stat.atime),
atimeMs: stat.atime.getTime(),
mtime: new Date(stat.mtime),
mtimeMs: stat.mtime.getTime(),
ctime: new Date(stat.ctime),
ctimMs: stat.ctime.getTime(),
birthtime: new Date(stat.birthtime),
birthtimeMs: stat.birthtime.getTime()
}
}
const createStat = function(extensions: any) {
return Object.assign(new fs.Stats(), binary.layout.stat, statTime(), extensions)
}
const ownStat = function(filepath: any) {
setupManifest()
const key = getKey(filepath)
if (directories[key]) {
let mode = binary.layout.stat.mode
mode |= fs.constants.S_IFDIR
mode &= ~fs.constants.S_IFREG
return createStat({ mode, size: 0 })
}
if (manifest[key]) {
return createStat({ size: manifest[key][1] })
}
}
function makeLong(filepath: string) {
return (path as any)._makeLong && (path as any)._makeLong(filepath)
}
function fileOpts(options: any) {
return !options ? {} : isString(options) ? { encoding: options } : options
}
let setupManifest = () => {
Object.keys(manifest).forEach(filepath => {
const entry = manifest[filepath]
const absolutePath = getKey(filepath)
const longPath = makeLong(absolutePath)
const normalizedPath = winPath(path.normalize(filepath))
if (!manifest[absolutePath]) {
manifest[absolutePath] = entry
}
if (longPath && !manifest[longPath]) {
manifest[longPath] = entry
}
if (!manifest[normalizedPath]) {
manifest[normalizedPath] = manifest[filepath]
}
let currentDir = path.dirname(absolutePath)
let prevDir = absolutePath
while (currentDir !== prevDir) {
directories[currentDir] = directories[currentDir] || {}
directories[currentDir][path.basename(prevDir)] = true
const longDir = makeLong(currentDir)
if (longDir && !directories[longDir]) {
directories[longDir] = directories[currentDir]
}
prevDir = currentDir
currentDir = path.dirname(currentDir)
}
})
;(manifest[notAFile] as any) = false
;(directories[notAFile] as any) = false
setupManifest = noop
}
//naive patches intended to work for most use cases
const nfs: any = {
existsSync: function existsSync(filepath: string) {
setupManifest()
const key = getKey(filepath)
if (manifest[key] || directories[key]) {
return true
}
return originalFsMethods.existsSync.apply(fs, arguments)
},
realpath: function realpath(filepath: any, options: any, cb: any): void {
setupManifest()
const key = getKey(filepath)
if (isString(filepath) && (manifest[filepath] || manifest[key])) {
return process.nextTick(() => cb(null, filepath))
}
return originalFsMethods.realpath.call(fs, filepath, options, cb)
},
realpathSync: function realpathSync(filepath: any, options: any) {
setupManifest()
const key = getKey(filepath)
if (manifest[key]) {
return filepath
}
return originalFsMethods.realpathSync.call(fs, filepath, options)
},
readdir: function readdir(filepath: string | Buffer, options: any, callback: any) {
setupManifest()
const dir = directories[getKey(filepath)]
if (dir) {
if ('function' === typeof options) {
callback = options
options = { encoding: 'utf8' }
}
process.nextTick(() => callback(null, Object.keys(dir)))
} else {
return originalFsMethods.readdir.apply(fs, arguments)
}
},
readdirSync: function readdirSync(filepath: string | Buffer, options: any) {
setupManifest()
const dir = directories[getKey(filepath)]
if (dir) {
return Object.keys(dir)
}
return originalFsMethods.readdirSync.apply(fs, arguments)
},
readFile: function readFile(filepath: any, options: any, callback: any) {
setupManifest()
const entry = manifest[getKey(filepath)]
if (!entry) {
return originalFsMethods.readFile.apply(fs, arguments)
}
const [offset, length] = entry
const resourceOffset = resourceStart + offset
const encoding = fileOpts(options).encoding
callback = typeof options === 'function' ? options : callback
originalFsMethods.open(blobPath, 'r', function(err: Error, fd: number) {
if (err) return callback(err, null)
originalFsMethods.read(fd, Buffer.alloc(length), 0, length, resourceOffset, function(
error: Error,
bytesRead: number,
result: Buffer
) {
if (error) {
return originalFsMethods.close(fd, function() {
callback(error, null)
})
}
originalFsMethods.close(fd, function(err: Error) {
if (err) {
return callback(err, result)
}
callback(err, encoding ? result.toString(encoding) : result)
})
})
})
},
createReadStream: function createReadStream(filepath: any, options: any) {
setupManifest()
const entry = manifest[getKey(filepath)]
if (!entry) {
return originalFsMethods.createReadStream.apply(fs, arguments)
}
const [offset, length] = entry
const resourceOffset = resourceStart + offset
const opts = fileOpts(options)
return originalFsMethods.createReadStream(
blobPath,
Object.assign({}, opts, {
start: resourceOffset,
end: resourceOffset + length - 1
})
)
},
readFileSync: function readFileSync(filepath: any, options: any) {
setupManifest()
const entry = manifest[getKey(filepath)]
if (!entry) {
return originalFsMethods.readFileSync.apply(fs, arguments)
}
const [offset, length] = entry
const resourceOffset = resourceStart + offset
const encoding = fileOpts(options).encoding
const fd = originalFsMethods.openSync(process.execPath, 'r')
const result = Buffer.alloc(length)
originalFsMethods.readSync(fd, result, 0, length, resourceOffset)
originalFsMethods.closeSync(fd)
return encoding ? result.toString(encoding) : result
},
statSync: function statSync(filepath: string | Buffer) {
const stat = ownStat(filepath)
if (stat) {
return stat
}
return originalFsMethods.statSync.apply(fs, arguments)
},
stat: function stat(filepath: string | Buffer, callback: any) {
const stat = ownStat(filepath)
if (stat) {
process.nextTick(() => {
callback(null, stat)
})
} else {
return originalFsMethods.stat.apply(fs, arguments)
}
}
}
if (typeof fs.exists === 'function') {
nfs.exists = function(filepath: string, cb: Function) {
cb = cb || noop
const exists = nfs.existsSync(filepath)
process.nextTick(() => cb(exists))
}
}
const patches = (process as any).nexe.patches || {}
delete (process as any).nexe
patches.internalModuleReadFile = function(this: any, original: any, ...args: any[]) {
const [filepath] = args
setupManifest()
if (manifest[filepath]) {
log('read (hit) ' + filepath)
return nfs.readFileSync(filepath, 'utf-8')
}
log('read (miss) ' + filepath)
return original.call(this, ...args)
}
patches.internalModuleReadJSON = patches.internalModuleReadFile
patches.internalModuleStat = function(this: any, original: any, ...args: any[]) {
setupManifest()
const [filepath] = args
if (manifest[filepath]) {
log('stat (hit) ' + filepath + ' ' + 0)
return 0
}
if (directories[filepath]) {
log('stat dir (hit) ' + filepath + ' ' + 1)
return 1
}
const res = original.call(this, ...args)
if (res === 0) {
log('stat (miss) ' + filepath + ' ' + res)
} else if (res === 1) {
log('stat dir (miss) ' + filepath + ' ' + res)
} else {
log('stat (fail) ' + filepath + ' ' + res)
}
return res
}
if (typeof fs.exists === 'function') {
nfs.exists = function(filepath: string, cb: Function) {
cb = cb || noop
const exists = nfs.existsSync(filepath)
if (!exists) {
return originalFsMethods.exists(filepath, cb)
}
process.nextTick(() => cb(exists))
}
}
if (typeof fs.copyFile === 'function') {
nfs.copyFile = function(filepath: string, dest: string, flags: number, callback: Function) {
setupManifest()
const entry = manifest[getKey(filepath)]
if (!entry) {
return originalFsMethods.copyFile.apply(fs, arguments)
}
if (typeof flags === 'function') {
callback = flags
flags = 0
}
nfs.readFile(filepath, (err: any, buffer: any) => {
if (err) {
return callback(err)
}
originalFsMethods.writeFile(dest, buffer, (err: any) => {
if (err) {
return callback(err)
}
callback(null)
})
})
}
nfs.copyFileSync = function(filepath: string, dest: string) {
setupManifest()
const entry = manifest[getKey(filepath)]
if (!entry) {
return originalFsMethods.copyFileSync.apply(fs, arguments)
}
return originalFsMethods.writeFileSync(dest, nfs.readFileSync(filepath))
}
}
Object.assign(fs, nfs)
lazyRestoreFs = () => {
Object.keys(nfs).forEach(key => {
fs[key] = originalFsMethods[key]
})
lazyRestoreFs = () => {}
}
return true
}
function restoreFs() {
lazyRestoreFs()
}
export { shimFs, restoreFs }
+72
View File
@@ -0,0 +1,72 @@
import colors from 'chalk'
import ora from 'ora'
const frameLength = 120
export interface LogStep {
modify(text: string, color?: string): void
log(text: string, color?: string): void
pause(): void
resume(): void
}
export class Logger {
public verbose: boolean
private silent: boolean
private ora: any
private modify: Function
public write: (text: string, color?: string) => void
constructor(level: 'verbose' | 'silent' | 'info') {
this.verbose = level === 'verbose'
this.silent = level === 'silent'
if (!this.silent) {
this.ora = ora({
text: 'Starting...',
color: 'blue',
spinner: 'dots'
})
this.ora.stop()
}
const noop = () => {}
this.modify = this.silent ? noop : this._modify.bind(this)
this.write = this.silent ? noop : this._write.bind(this)
}
flush() {
!this.silent && this.ora.succeed()
return new Promise(resolve => setTimeout(resolve, frameLength))
}
_write(update: string, color = 'green') {
this.ora.succeed().text = (colors as any)[color](update)
this.ora.start()
}
_modify(update: string, color = this.ora.color) {
this.ora.text = update
this.ora.color = color
}
step(text: string, method: string = 'succeed'): LogStep {
if (this.silent) {
return { modify() {}, log() {}, pause() {}, resume() {} }
}
if (!this.ora.id) {
this.ora.start().text = text
if (method !== 'succeed') {
this.ora[method]()
}
} else {
this.ora[method]().text = text
this.ora.start()
}
return {
modify: this.modify,
log: this.verbose ? this.write : this.modify,
pause: () => this.ora.stopAndPersist(),
resume: () => this.ora.start()
} as LogStep
}
}
+49
View File
@@ -0,0 +1,49 @@
import { EOL } from 'os'
import { compose } from 'app-builder'
import { NexeCompiler, NexeError } from './compiler'
import { normalizeOptions, NexeOptions, NexePatch } from './options'
import resource from './steps/resource'
import clean from './steps/clean'
import cli from './steps/cli'
import bundle from './steps/bundle'
import download from './steps/download'
import shim from './steps/shim'
import artifacts from './steps/artifacts'
import patches from './patches'
async function compile(
compilerOptions?: Partial<NexeOptions>,
callback?: (err: Error | null) => void
) {
let error = null,
options: NexeOptions | null = null,
compiler: NexeCompiler | null = null
try {
options = normalizeOptions(compilerOptions)
compiler = new NexeCompiler(options)
await compose(
clean,
resource,
cli,
bundle,
shim,
download,
options.build ? [artifacts, ...patches, ...(options.patches as NexePatch[])] : [],
options.plugins as NexePatch[]
)(compiler)
} catch (e) {
error = e
}
if (error) {
compiler && compiler.quit(error)
if (callback) return callback(error)
return Promise.reject(error)
}
if (callback) callback(null)
}
export { compile, NexeCompiler }
export { argv, version, NexeOptions, help } from './options'
+301
View File
@@ -0,0 +1,301 @@
import * as parseArgv from 'minimist'
import { NexeCompiler, NexeError } from './compiler'
import { isWindows, STDIN_FLAG } from './util'
import { basename, extname, join, isAbsolute, resolve } from 'path'
import { getTarget, NexeTarget } from './target'
import { EOL, homedir } from 'os'
import chalk from 'chalk'
import { resolveFileNameSync } from 'resolve-dependencies'
const caw = require('caw')
const c = process.platform === 'win32' ? chalk.constructor({ enabled: false }) : chalk
export const version = '{{replace:0}}'
export interface NexePatch {
(compiler: NexeCompiler, next: () => Promise<void>): Promise<void>
}
export interface NexeOptions {
build: boolean
input: string
output: string
targets: (string | NexeTarget)[]
name: string
asset: string
cwd: string
fs: boolean | string[]
flags: string[]
configure: string[]
vcBuild: string[]
make: string[]
snapshot?: string
resources: string[]
temp: string
rc: { [key: string]: string }
enableNodeCli: boolean
bundle: boolean | string
patches: (string | NexePatch)[]
plugins: (string | NexePatch)[]
native: any
mangle: boolean
ghToken: string
sourceUrl?: string
enableStdIn?: boolean
python?: string
loglevel: 'info' | 'silent' | 'verbose'
silent?: boolean
fakeArgv?: boolean
verbose?: boolean
info?: boolean
ico?: string
debugBundle?: boolean
warmup?: string
clean?: boolean
/**
* Api Only
*/
downloadOptions: any
}
const defaults = {
flags: [],
cwd: process.cwd(),
fs: true,
configure: [],
mangle: true,
make: [],
targets: [],
vcBuild: isWindows ? ['nosign', 'release'] : [],
enableNodeCli: false,
build: false,
bundle: true,
patches: [],
plugins: []
}
const alias = {
i: 'input',
o: 'output',
v: 'version',
t: 'target',
b: 'build',
n: 'name',
r: 'resource',
a: 'asset',
p: 'python',
f: 'flag',
c: 'configure',
m: 'make',
h: 'help',
l: 'loglevel',
'fake-argv': 'fakeArgv',
'gh-token': 'ghToken'
}
const argv = parseArgv(process.argv, { alias, default: { ...defaults } })
let help = `
${c.bold('nexe <entry-file> [options]')}
${c.underline.bold('Options:')}
-i --input -- application entry point
-o --output -- path to output file
-t --target -- node version description
-n --name -- main app module name
-r --resource -- *embed files (glob) within the binary
-a --asset -- alternate asset path, file or url pointing to a base (nexe) binary
--plugin -- extend nexe runtime behavior
${c.underline.bold('Building from source:')}
-b --build -- build from source
-p --python -- python2 (as python) executable path
-f --flag -- *v8 flags to include during compilation
-c --configure -- *arguments to the configure step
-m --make -- *arguments to the make/build step
--patch -- module with middleware default export for adding a build patch
--no-mangle -- used when generating base binaries, or when patching _third_party_main manually.
--snapshot -- path to a warmup snapshot
--ico -- file name for alternate icon file (windows)
--rc-* -- populate rc file options (windows)
--sourceUrl -- pass an alternate source (node.tar.gz) url
--enableNodeCli -- enable node cli enforcement (blocks app cli)
${c.underline.bold('Other options:')}
--bundle -- custom bundling module with 'createBundle' export
--temp -- temp file storage default '~/.nexe'
--cwd -- set the current working directory for the command
--fake-argv -- fake argv[1] with entry file
--clean -- force download of sources
--silent -- disable logging
--verbose -- set logging to verbose
-* variable key name * option can be used more than once`.trim()
help = EOL + help + EOL
function flatten(...args: any[]): string[] {
return ([] as string[]).concat(...args).filter(x => x)
}
/**
* Extract keys such as { "rc-CompanyName": "Node.js" } to
* { CompanyName: "Node.js" }
* @param {*} match
* @param {*} options
*/
function extractCliMap(match: RegExp, options: any) {
return Object.keys(options)
.filter(x => match.test(x))
.reduce(
(map: any, option: any) => {
const key = option.split('-')[1]
map[key] = options[option]
delete options[option]
return map
},
{} as any
)
}
function extractLogLevel(options: NexeOptions) {
if (options.loglevel) return options.loglevel
if (options.silent) return 'silent'
if (options.verbose) return 'verbose'
return 'info'
}
function isName(name: string) {
return name && name !== 'index' && name !== STDIN_FLAG
}
function extractName(options: NexeOptions) {
let name = options.name
//try and use the input filename as the output filename if its not index
if (!isName(name) && typeof options.input === 'string') {
name = basename(options.input).replace(extname(options.input), '')
}
//try and use the directory as the filename
if (!isName(name) && basename(options.cwd)) {
name = basename(options.cwd)
}
return name.replace(/\.exe$/, '')
}
function padRelative(input: string) {
let prefix = ''
if (!input.startsWith('.')) {
prefix = './'
}
return prefix + input
}
function isEntryFile(filename?: string): filename is string {
return Boolean(filename && !isAbsolute(filename))
}
export function resolveEntry(
input: string,
cwd: string,
maybeEntry: string | undefined,
bundle: boolean | string
) {
let result = null
if (input === '-' || maybeEntry === '-') {
return STDIN_FLAG
}
if (input && isAbsolute(input)) {
return input
}
if (input) {
const inputPath = padRelative(input)
result = resolveFileNameSync(cwd, inputPath)
}
if (isEntryFile(maybeEntry) && (!result || !result.absPath)) {
const inputPath = padRelative(maybeEntry)
result = resolveFileNameSync(cwd, inputPath)
}
if (!process.stdin.isTTY && (!result || !result.absPath) && bundle === defaults.bundle) {
return STDIN_FLAG
}
if (!result || !result.absPath) {
result = resolveFileNameSync(cwd, '.')
}
if (!result.absPath) {
throw new NexeError(`Entry file "${input || ''}" not found!`)
}
return result.absPath
}
function isCli(options?: Partial<NexeOptions>) {
return argv === options
}
function normalizeOptions(input?: Partial<NexeOptions>): NexeOptions {
const options = Object.assign({}, defaults, input) as NexeOptions
const opts = options as any
const cwd = (options.cwd = resolve(options.cwd))
options.temp = options.temp
? resolve(cwd, options.temp)
: process.env.NEXE_TEMP || join(homedir(), '.nexe')
const maybeEntry = isCli(input) ? argv._[argv._.length - 1] : undefined
options.input = resolveEntry(options.input, cwd, maybeEntry, options.bundle)
options.enableStdIn = isCli(input) && options.input === STDIN_FLAG
options.name = extractName(options)
options.loglevel = extractLogLevel(options)
options.flags = flatten(opts.flag, options.flags)
options.targets = flatten(opts.target, options.targets).map(getTarget)
if (!options.targets.length) {
options.targets.push(getTarget())
}
options.ghToken = options.ghToken || process.env.GITHUB_TOKEN || ''
options.make = flatten(isWindows ? options.vcBuild : options.make)
options.configure = flatten(options.configure)
options.resources = flatten(opts.resource, options.resources)
options.downloadOptions = options.downloadOptions || {}
options.downloadOptions.headers = options.downloadOptions.headers || {}
options.downloadOptions.headers['User-Agent'] = 'nexe (https://www.npmjs.com/package/nexe)'
options.downloadOptions.agent = process.env.HTTPS_PROXY
? caw(process.env.HTTPS_PROXY, { protocol: 'https' })
: options.downloadOptions.agent || require('https').globalAgent
options.downloadOptions.rejectUnauthorized = process.env.HTTPS_PROXY ? false : true
options.rc = options.rc || extractCliMap(/^rc-.*/, options)
options.output =
(options.targets[0] as NexeTarget).platform === 'windows'
? `${(options.output || options.name).replace(/\.exe$/, '')}.exe`
: `${options.output || options.name}`
options.output = resolve(cwd, options.output)
const requireDefault = (x: string) => {
if (typeof x === 'string') {
return require(x).default
}
return x
}
options.mangle = 'mangle' in opts ? opts.mangle : true
options.plugins = flatten(opts.plugin, options.plugins).map(requireDefault)
options.patches = flatten(opts.patch, options.patches).map(requireDefault)
if ((!options.mangle && !options.bundle) || options.patches.length) {
options.build = true
}
if (options.build) {
const { arch } = options.targets[0] as NexeTarget
if (isWindows) {
options.make = Array.from(new Set(options.make.concat(arch)))
} else {
options.configure = Array.from(new Set(options.configure.concat([`--dest-cpu=${arch}`])))
}
}
Object.keys(alias)
.filter(k => k !== 'rc')
.forEach(x => delete opts[x])
return options
}
export { argv, normalizeOptions, help }
+57
View File
@@ -0,0 +1,57 @@
const fs = require('fs'),
fd = fs.openSync(process.execPath, 'r'),
stat = fs.statSync(process.execPath),
tailSize = Math.min(stat.size, 16000),
tailWindow = Buffer.from(Array(tailSize))
fs.readSync(fd, tailWindow, 0, tailSize, stat.size - tailSize)
const footerPosition = tailWindow.indexOf('<nexe~~sentinel>')
if (footerPosition == -1) {
throw 'Invalid Nexe binary'
}
const footer = tailWindow.slice(footerPosition, footerPosition + 32),
contentSize = footer.readDoubleLE(16),
resourceSize = footer.readDoubleLE(24),
contentStart = stat.size - tailSize + footerPosition - resourceSize - contentSize,
resourceStart = contentStart + contentSize
Object.defineProperty(
process,
'__nexe',
(function() {
let nexeHeader: any = null
return {
get: function() {
return nexeHeader
},
set: function(value: any) {
if (nexeHeader) {
throw new Error('This property is readonly')
}
nexeHeader = Object.assign({}, value, {
blobPath: process.execPath,
layout: {
stat,
contentSize,
contentStart,
resourceSize,
resourceStart
}
})
Object.freeze(nexeHeader)
},
enumerable: false,
configurable: false
}
})()
)
const contentBuffer = Buffer.from(Array(contentSize)),
Module = require('module')
fs.readSync(fd, contentBuffer, 0, contentSize, contentStart)
fs.closeSync(fd)
new Module(process.execPath, null)._compile(contentBuffer.toString(), process.execPath)
+17
View File
@@ -0,0 +1,17 @@
import { NexeCompiler } from '../compiler'
export default async function buildFixes(compiler: NexeCompiler, next: () => Promise<void>) {
if (!compiler.target.version.startsWith('8.2')) {
return next()
}
const file = await compiler.readFileAsync('./tools/msvs/find_python.cmd')
await compiler.replaceInFileAsync(
'./tools/msvs/find_python.cmd',
'%p%python.exe -V 2>&1',
'"%p%python.exe" -V 2>&1'
)
return next()
}
+44
View File
@@ -0,0 +1,44 @@
import { NexeCompiler } from '../compiler'
import { semverGt } from '../util'
export default async function disableNodeCli(compiler: NexeCompiler, next: () => Promise<void>) {
if (compiler.options.enableNodeCli) {
return next()
}
if (semverGt(compiler.target.version, '11.6.0')) {
await compiler.replaceInFileAsync(
'src/node.cc',
/(?<!int )ProcessGlobalArgs\(/g,
'0;//ProcessGlobalArgs('
)
} else if (semverGt(compiler.target.version, '10.9')) {
await compiler.replaceInFileAsync('src/node.cc', /(?<!void )ProcessArgv\(/g, '//ProcessArgv(')
} else if (semverGt(compiler.target.version, '9.999')) {
await compiler.replaceInFileAsync(
'src/node.cc',
'int i = 1; i < v8_argc; i++',
'int i = v8_argc; i < v8_argc; i++'
)
let matches = 0
await compiler.replaceInFileAsync('src/node.cc', /v8_argc > 1/g, match => {
if (matches++) {
return 'false'
}
return match
})
} else {
const nodeccMarker = 'argv[index][0] =='
await compiler.replaceInFileAsync(
'src/node.cc',
`${nodeccMarker} '-'`,
// allow NODE_OPTIONS, introduced in 8.0
semverGt(compiler.target.version, '7.99')
? `(${nodeccMarker} (is_env ? '-' : ']'))`
: `(${nodeccMarker} ']')`
)
}
return next()
}
+16
View File
@@ -0,0 +1,16 @@
import { NexeCompiler } from '../compiler'
export default async function flags(compiler: NexeCompiler, next: () => Promise<void>) {
const nodeflags = compiler.options.flags
if (!nodeflags.length) {
return next()
}
await compiler.replaceInFileAsync(
'node.gyp',
"'node_v8_options%': ''",
`'node_v8_options%': '${nodeflags.join(' ')}'`
)
return next()
}
+21
View File
@@ -0,0 +1,21 @@
import { NexeCompiler } from '../compiler'
export default async function nodeGyp(
{ files, replaceInFileAsync }: NexeCompiler,
next: () => Promise<void>
) {
await next()
const nodeGypMarker = "'lib/fs.js',"
await replaceInFileAsync(
'node.gyp',
nodeGypMarker,
`
${nodeGypMarker}
${files
.filter(x => x.filename.startsWith('lib'))
.map(x => `'${x.filename}'`)
.toString()},
`.trim()
)
}
+12
View File
@@ -0,0 +1,12 @@
import { normalize } from 'path'
import { readFileAsync } from '../util'
import { NexeCompiler } from '../compiler'
export default async function ico(compiler: NexeCompiler, next: () => Promise<void>) {
const iconFile = compiler.options.ico
if (!iconFile) {
return next()
}
await compiler.setFileContentsAsync('src/res/node.ico', await readFileAsync(normalize(iconFile)))
return next()
}
+12
View File
@@ -0,0 +1,12 @@
import gyp from './gyp'
import bootNexe from './third-party-main'
import buildFixes from './build-fixes'
import cli from './disable-node-cli'
import flags from './flags'
import ico from './ico'
import rc from './node-rc'
import snapshot from './snapshot'
const patches = [gyp, bootNexe, buildFixes, cli, flags, ico, rc, snapshot]
export default patches
+27
View File
@@ -0,0 +1,27 @@
import { NexeCompiler } from '../compiler'
export default async function nodeRc(compiler: NexeCompiler, next: () => Promise<void>) {
const options = compiler.options.rc
if (!options) {
return next()
}
const file = await compiler.readFileAsync('src/res/node.rc')
Object.keys(options).forEach(key => {
let value = options[key]
const isVar = /^[A-Z_]+$/.test(value)
value = isVar ? value : `"${value}"`
file.contents = file.contents.replace(
new RegExp(`VALUE "${key}",.*`),
`VALUE "${key}", ${value}`
)
})
;['PRODUCTVERSION', 'FILEVERSION'].forEach(x => {
if (options[x]) {
file.contents = file.contents.replace(new RegExp(x + ' .*$', 'm'), `${x} ${options[x]}`)
}
})
return next()
}
+27
View File
@@ -0,0 +1,27 @@
import { resolve } from 'path'
import { NexeCompiler } from '../compiler'
import { semverGt } from '../util'
export default async function(compiler: NexeCompiler, next: () => Promise<void>) {
const { snapshot, warmup, cwd } = compiler.options
if (!snapshot) {
return next()
}
const variablePrefix = semverGt(compiler.target.version, '11.0.0') ? 'v8_' : ''
await compiler.replaceInFileAsync(
compiler.configureScript,
'def configure_v8(o):',
`def configure_v8(o):\n o['variables']['${variablePrefix}embed_script'] = r'${resolve(
cwd,
snapshot
)}'\n o['variables']['${variablePrefix}warmup_script'] = r'${resolve(
cwd,
warmup || snapshot
)}'`
)
return next()
}
+86
View File
@@ -0,0 +1,86 @@
import { NexeCompiler } from '../compiler'
import { parse } from 'cherow'
import { wrap, semverGt } from '../util'
function walkSome(node: any, visit: Function) {
if (!node || typeof node.type !== 'string' || node._visited) {
return false
}
visit(node)
node._visited = true
for (let childNode in node) {
const child = node[childNode]
if (Array.isArray(child)) {
for (let i = 0; i < child.length; i++) {
if (walkSome(child[i], visit)) {
return true
}
}
} else if (walkSome(child, visit)) {
return true
}
}
return false
}
export default async function main(compiler: NexeCompiler, next: () => Promise<void>) {
let bootFile = 'lib/internal/bootstrap_node.js'
const { version } = compiler.target
if (version.startsWith('4.')) {
bootFile = 'src/node.js'
} else if (semverGt(version, '11.99')) {
bootFile = 'lib/internal/bootstrap/pre_execution.js'
} else if (semverGt(version, '9.10.1')) {
bootFile = 'lib/internal/bootstrap/node.js'
}
const file = await compiler.readFileAsync(bootFile),
ast = parse(file.contents, {
loc: true,
tolerant: true,
next: true,
globalReturn: true,
node: true,
skipShebang: true
}),
location = { start: { line: 0 } }
walkSome(ast, (node: any) => {
if (!location.start.line && node.type === 'BlockStatement') {
//Find the first block statement and mark the location
Object.assign(location, node.loc)
return true
}
})
const fileLines = file.contents.split('\n')
fileLines.splice(
location.start.line,
0,
'{{replace:lib/fs/bootstrap.js}}' +
'\n' +
(semverGt(version, '11.99') ? 'expandArgv1 = false;\n' : '')
)
file.contents = fileLines.join('\n')
if (semverGt(version, '11.99')) {
await compiler.replaceInFileAsync(
bootFile,
'initializePolicy();',
'initializePolicy();\n' + wrap('{{replace:lib/patches/boot-nexe.js}}')
)
await compiler.replaceInFileAsync(
'src/node.cc',
'MaybeLocal<Value> StartMainThreadExecution(Environment* env) {',
'MaybeLocal<Value> StartMainThreadExecution(Environment* env) {\n' +
' return StartExecution(env, "internal/main/run_main_module");\n'
)
} else {
await compiler.setFileContentsAsync(
'lib/_third_party_main.js',
'{{replace:lib/patches/boot-nexe.js}}'
)
}
return next()
}
+60
View File
@@ -0,0 +1,60 @@
import got = require('got')
import { platforms, architectures, NexeTarget, getTarget, targetsEqual } from './target'
export { NexeTarget }
const versionsToSkip = [0, 1, 2, 3, 4, 5, 7, 9, 11]
export interface GitAsset {
name: string
url: string
browser_download_url: string
}
export interface GitRelease {
tag_name: string
assets_url: string
upload_url: string
assets: GitAsset[]
}
interface NodeRelease {
version: string
}
async function getJson<T>(url: string, options?: any) {
return JSON.parse((await got(url, options)).body) as T
}
function isBuildableVersion(version: string) {
const major = Number(version.split('.')[0])
return !versionsToSkip.includes(major) || version === '4.8.4'
}
export function getLatestGitRelease(options?: any) {
return getJson<GitRelease>('https://api.github.com/repos/nexe/nexe/releases/latest', options)
}
export async function getUnBuiltReleases(options?: any) {
const nodeReleases = await getJson<NodeRelease[]>(
'https://nodejs.org/download/release/index.json'
)
const existingVersions = (await getLatestGitRelease(options)).assets.map(x => getTarget(x.name))
const versionMap: { [key: string]: true } = {}
return nodeReleases
.reduce((versions: NexeTarget[], { version }) => {
version = version.replace('v', '').trim()
if (!isBuildableVersion(version) || versionMap[version]) {
return versions
}
versionMap[version] = true
platforms.forEach(platform => {
architectures.forEach(arch => {
if (arch === 'x86' && platform === 'mac') return
if (arch.includes('arm')) return
versions.push(getTarget({ platform, arch, version }))
})
})
return versions
}, [])
.filter(x => !existingVersions.some(t => targetsEqual(t, x)))
}
+69
View File
@@ -0,0 +1,69 @@
import { join, dirname } from 'path'
import { readdir, unlink } from 'fs'
import { readFileAsync, writeFileAsync, isDirectoryAsync } from '../util'
import * as mkdirp from 'mkdirp'
import { NexeCompiler } from '../compiler'
import pify = require('pify')
const mkdirpAsync = pify(mkdirp)
const unlinkAsync = pify(unlink)
const readdirAsync = pify(readdir)
function readDirAsync(dir: string): Promise<string[]> {
return readdirAsync(dir).then(paths => {
return Promise.all(
paths.map((file: string) => {
const path = join(dir, file)
return isDirectoryAsync(path).then(x => (x ? readDirAsync(path) : (path as any)))
})
).then(result => {
return [].concat(...(result as any))
})
})
}
function maybeReadFileContentsAsync(file: string) {
return readFileAsync(file, 'utf-8').catch(e => {
if (e.code === 'ENOENT') {
return ''
}
throw e
})
}
/**
* The artifacts step is where source patches are committed, or written as "artifacts"
* Steps:
* - A temporary directory is created in the downloaded source
* - On start, any files in that directory are restored into the source tree
* - After the patch functions have run, the temporary directory is emptied
* - Original versions of sources to be patched are written to the temporary directory
* - Finally, The patched files are written into source.
*/
export default async function artifacts(compiler: NexeCompiler, next: () => Promise<void>) {
const { src } = compiler
const temp = join(src, 'nexe')
await mkdirpAsync(temp)
const tmpFiles = await readDirAsync(temp)
await Promise.all(
tmpFiles.map(async path => {
return compiler.writeFileAsync(path.replace(temp, ''), await readFileAsync(path, 'utf-8'))
})
)
await next()
await Promise.all(tmpFiles.map(x => unlinkAsync(x)))
return Promise.all(
compiler.files.map(async file => {
const sourceFile = join(src, file.filename)
const tempFile = join(temp, file.filename)
const fileContents = await maybeReadFileContentsAsync(sourceFile)
await mkdirpAsync(dirname(tempFile))
await writeFileAsync(tempFile, fileContents)
await compiler.writeFileAsync(file.filename, file.contents)
})
)
}
+81
View File
@@ -0,0 +1,81 @@
import { NexeCompiler, NexeError } from '../compiler'
import { resolve, relative } from 'path'
import { each } from '@calebboyd/semaphore'
import resolveFiles, { resolveFileNameSync } from 'resolve-dependencies'
import { dequote, STDIN_FLAG, semverGt } from '../util'
import { Readable } from 'stream'
function getStdIn(stdin: Readable): Promise<string> {
let out = ''
return new Promise(resolve => {
stdin
.setEncoding('utf8')
.on('readable', () => {
let current
while ((current = stdin.read())) {
out += current
}
})
.on('end', () => resolve(out.trim()))
setTimeout(() => {
if (!out.trim()) {
resolve(out.trim())
}
}, 1000)
})
}
export default async function bundle(compiler: NexeCompiler, next: any) {
const { bundle, cwd, input: inputPath } = compiler.options
let input = inputPath
compiler.entrypoint = './' + relative(cwd, input)
if (semverGt(compiler.target.version, '11.99')) {
compiler.startup = ''
} else {
compiler.startup = ';require("module").runMain();'
}
if (!bundle) {
await compiler.addResource(resolve(cwd, input))
return next()
}
let code = ''
if (typeof bundle === 'string') {
code = await require(bundle).createBundle(compiler.options)
}
if (input === STDIN_FLAG && (code = code || dequote(await getStdIn(process.stdin)))) {
compiler.stdinUsed = true
compiler.entrypoint = './__nexe_stdin.js'
await compiler.addResource(resolve(cwd, compiler.entrypoint), code)
return next()
}
if (input === STDIN_FLAG) {
const maybeInput = resolveFileNameSync(cwd, '.')
if (!maybeInput || !maybeInput.absPath) {
throw new NexeError('No valid input detected')
}
input = maybeInput.absPath
compiler.entrypoint = './' + relative(cwd, input)
}
const { files, warnings } = await resolveFiles(
input,
...Object.keys(compiler.bundle.index).filter(x => x.endsWith('.js')),
{ cwd, expand: true, loadContent: false }
)
if (
warnings.filter(x => x.startsWith('Error parsing file') && !x.includes('node_modules')).length
) {
throw new NexeError('Parsing Error:\n' + warnings.join('\n'))
}
await each(Object.keys(files), (filename: string) => compiler.addResource(filename), {
concurrency: 10
})
return next()
}
+19
View File
@@ -0,0 +1,19 @@
import { NexeCompiler } from '../compiler'
import { rimrafAsync } from '../util'
import { NexeTarget } from '../target'
export default async function clean(compiler: NexeCompiler, next: () => Promise<any>) {
const { options } = compiler
if (options.clean) {
let path = compiler.src
if (!options.build) {
path = compiler.getNodeExecutableLocation(compiler.options.targets[0] as NexeTarget)
}
const step = compiler.log.step('Cleaning up nexe build artifacts...')
step.log(`Deleting contents at: ${path}`)
await rimrafAsync(path)
step.log(`Deleted contents at: ${path}`)
return compiler.quit()
}
return next()
}
+59
View File
@@ -0,0 +1,59 @@
import { dirname, normalize, relative, resolve } from 'path'
import { createWriteStream, chmodSync, statSync } from 'fs'
import { NexeCompiler } from '../compiler'
import { NexeTarget } from '../target'
import { STDIN_FLAG } from '../util'
import mkdirp = require('mkdirp')
/**
* The "cli" step detects the appropriate input. If no input options are passed,
* the package.json#main file is used.
* After all the build steps have run, the output (the executable) is written to a file or piped to stdout.
*
* Configuration:
*
* @param {*} compiler
* @param {*} next
*/
export default async function cli(compiler: NexeCompiler, next: () => Promise<void>) {
await next()
const { log } = compiler,
target = compiler.options.targets.shift() as NexeTarget,
deliverable = await compiler.compileAsync(target),
output = normalize(compiler.output!)
mkdirp.sync(dirname(output))
return new Promise((res, rej) => {
const step = log.step('Writing result to file')
deliverable
.pipe(createWriteStream(output))
.on('error', rej)
.once('close', (e: Error) => {
if (e) {
rej(e)
} else if (compiler.output) {
const output = compiler.output,
mode = statSync(output).mode | 0o111,
inputFileLogOutput = relative(
process.cwd(),
resolve(compiler.options.cwd, compiler.entrypoint || compiler.options.input)
),
outputFileLogOutput = relative(process.cwd(), output)
chmodSync(output, mode.toString(8).slice(-3))
step.log(
`Entry: '${
compiler.stdinUsed
? compiler.options.mangle
? STDIN_FLAG
: '[none]'
: inputFileLogOutput
}' written to: ${outputFileLogOutput}`
)
compiler.quit()
res(output)
}
})
})
}
+78
View File
@@ -0,0 +1,78 @@
import download = require('download')
import { pathExistsAsync } from '../util'
import { LogStep } from '../logger'
import { IncomingMessage } from 'http'
import { NexeCompiler, NexeError } from '../compiler'
import { dirname } from 'path'
function fetchNodeSourceAsync(dest: string, url: string, step: LogStep, options = {}) {
const setText = (p: number) => step.modify(`Downloading Node: ${p.toFixed()}%...`)
return download(url, dest, Object.assign(options, { extract: true, strip: 1 }))
.on('response', (res: IncomingMessage) => {
const total = +res.headers['content-length']!
let current = 0
res.on('data', data => {
current += data.length
setText((current / total) * 100)
if (current === total) {
step.log('Extracting Node...')
}
})
})
.then(() => step.log(`Node source extracted to: ${dest}`))
}
async function fetchPrebuiltBinary(compiler: NexeCompiler, step: any) {
const { target, remoteAsset } = compiler,
filename = compiler.getNodeExecutableLocation(target)
try {
await download(remoteAsset, dirname(filename), compiler.options.downloadOptions).on(
'response',
(res: IncomingMessage) => {
const total = +res.headers['content-length']!
let current = 0
res.on('data', data => {
current += data.length
step!.modify(`Downloading...${((current / total) * 100).toFixed()}%`)
})
}
)
} catch (e) {
if (e.statusCode === 404) {
throw new NexeError(`${remoteAsset} is not available, create it using the --build flag`)
} else {
throw new NexeError('Error downloading prebuilt binary: ' + e)
}
}
}
/**
* Downloads the node source to the configured temporary directory
* @param {*} compiler
* @param {*} next
*/
export default async function downloadNode(compiler: NexeCompiler, next: () => Promise<void>) {
const { src, log, target } = compiler,
{ version } = target,
{ sourceUrl, downloadOptions, build } = compiler.options,
url = sourceUrl || `https://nodejs.org/dist/v${version}/node-v${version}.tar.gz`,
step = log.step(
`Downloading ${build ? '' : 'pre-built '}Node.js${build ? `source from: ${url}` : ''}`
),
exeLocation = compiler.getNodeExecutableLocation(build ? undefined : target),
downloadExists = await pathExistsAsync(build ? src : exeLocation)
if (downloadExists) {
step.log('Already downloaded...')
return next()
}
if (build) {
await fetchNodeSourceAsync(src, url, step, downloadOptions)
} else {
await fetchPrebuiltBinary(compiler, step)
}
return next()
}
+22
View File
@@ -0,0 +1,22 @@
import { isDirectoryAsync, each } from '../util'
import globs from 'globby'
import { NexeCompiler } from '../compiler'
export default async function resource(compiler: NexeCompiler, next: () => Promise<any>) {
const { cwd } = compiler.options
if (!compiler.options.resources.length) {
return next()
}
const step = compiler.log.step('Bundling Resources...')
let count = 0
await each(globs(compiler.options.resources, { cwd }), async file => {
if (await isDirectoryAsync(file)) {
return
}
count++
step.log(`Including file: ${file}`)
await compiler.addResource(file)
})
step.log(`Included ${count} file(s). ${(compiler.resourceSize / 1e6).toFixed(3)} MB`)
return next()
}
+37
View File
@@ -0,0 +1,37 @@
import { NexeCompiler } from '../compiler'
import { wrap } from '../util'
export default function(compiler: NexeCompiler, next: () => Promise<void>) {
compiler.shims.push(
wrap(
`process.__nexe = ${JSON.stringify(compiler.binaryConfiguration)};\n` +
'{{replace:lib/fs/patch.js}}' +
'\nshimFs(process.__nexe)' +
`\n${compiler.options.fs ? '' : 'restoreFs()'}`
//TODO support only restoring specific methods
)
)
compiler.shims.push(
wrap(`
if (process.argv[1] && process.env.NODE_UNIQUE_ID) {
const cluster = require('cluster')
cluster._setupWorker()
delete process.env.NODE_UNIQUE_ID
}
`)
)
compiler.shims.push(
wrap(`
if (!process.send) {
const path = require('path')
const entry = path.resolve(path.dirname(process.execPath),${JSON.stringify(
compiler.entrypoint
)})
process.argv.splice(1,0, entry)
}
`)
)
return next()
}
+96
View File
@@ -0,0 +1,96 @@
export type NodePlatform = 'windows' | 'mac' | 'alpine' | 'linux'
export type NodeArch = 'x86' | 'x64' | 'arm7l' | 'arm6l' | 'arm64'
const platforms: NodePlatform[] = ['windows', 'mac', 'alpine', 'linux'],
architectures: NodeArch[] = ['x86', 'x64', 'arm7l', 'arm6l', 'arm64']
export { platforms, architectures }
export interface NexeTarget {
version: string
platform: NodePlatform | string
arch: NodeArch | string
}
const prettyPlatform: { [key: string]: NodePlatform } = {
win32: 'windows',
windows: 'windows',
win: 'windows',
darwin: 'mac',
macos: 'mac',
mac: 'mac',
linux: 'linux',
static: 'alpine',
alpine: 'alpine'
}
const prettyArch: { [key: string]: NodeArch } = {
x86: 'x86',
arm6: 'arm6l',
arm64: 'arm64',
arm6l: 'arm6l',
arm: 'arm7l',
arm7: 'arm7l',
arm7l: 'arm7l',
amd64: 'x64',
ia32: 'x86',
x32: 'x86',
x64: 'x64'
}
function isVersion(x: string) {
if (!x) {
return false
}
return /^[\d]+$/.test(x.replace(/v|\.|\s+/g, ''))
}
function isPlatform(x: string): x is NodePlatform {
return x in prettyPlatform
}
function isArch(x: string): x is NodeArch {
return x in prettyArch
}
class Target implements NexeTarget {
constructor(public arch: NodeArch, public platform: NodePlatform, public version: string) {}
toJSON() {
return this.toString()
}
toString() {
return `${this.platform}-${this.arch}-${this.version}`
}
}
export function targetsEqual(a: NexeTarget, b: NexeTarget) {
return a.arch === b.arch && a.platform === b.platform && a.version === b.version
}
export function getTarget(target: string | Partial<NexeTarget> = ''): NexeTarget {
const currentArch = process.arch
let arch = currentArch in prettyArch ? prettyArch[process.arch] : (process.arch as NodeArch),
platform = prettyPlatform[process.platform],
version = process.version.slice(1)
if (typeof target !== 'string') {
target = `${target.platform}-${target.arch}-${target.version}`
}
target
.toLowerCase()
.split('-')
.forEach(x => {
if (isVersion(x)) {
version = x.replace(/v/g, '')
}
if (isPlatform(x)) {
platform = prettyPlatform[x]
}
if (isArch(x)) {
arch = prettyArch[x]
}
})
return new Target(arch, platform, version)
}
+23
View File
@@ -0,0 +1,23 @@
declare module 'got' {
interface GotFn {
(url: string, options?: any): Promise<{ body: string }>
stream(url: string, optoins?: any): any
}
const got: GotFn
export = got
}
declare module 'download' {
import { Duplex } from 'stream'
interface DownloadOptions {
extract?: boolean
strip?: number
filename?: string
proxy?: string
}
function download(
url: string,
destination?: string | DownloadOptions,
options?: DownloadOptions
): PromiseLike<Buffer> & Duplex
export = download
}
+113
View File
@@ -0,0 +1,113 @@
import { readFile, writeFile, stat } from 'fs'
import { execFile } from 'child_process'
import pify = require('pify')
import rimraf = require('rimraf')
const rimrafAsync = pify(rimraf)
export const STDIN_FLAG = '[stdin]'
export async function each<T>(
list: T[] | Promise<T[]>,
action: (item: T, index: number, list: T[]) => Promise<any>
) {
const l = await list
return Promise.all(l.map(action))
}
export function wrap(code: string) {
return '!(function () {' + code + '})();'
}
function falseOnEnoent(e: any) {
if (e.code === 'ENOENT') {
return false
}
throw e
}
function padRight(str: string, l: number) {
return (str + ' '.repeat(l)).substr(0, l)
}
const bound: MethodDecorator = function bound<T>(
target: Object,
propertyKey: string | Symbol,
descriptor: TypedPropertyDescriptor<T>
) {
const configurable = true
return {
configurable,
get(this: T) {
const value = (descriptor.value as any).bind(this)
Object.defineProperty(this, propertyKey as string, {
configurable,
value,
writable: true
})
return value
}
}
}
function dequote(input: string) {
input = input.trim()
const singleQuote = input.startsWith("'") && input.endsWith("'")
const doubleQuote = input.startsWith('"') && input.endsWith('"')
if (singleQuote || doubleQuote) {
return input.slice(1).slice(0, -1)
}
return input
}
export interface ReadFileAsync {
(path: string): Promise<Buffer>
(path: string, encoding: string): Promise<string>
}
const readFileAsync = pify(readFile)
const writeFileAsync = pify(writeFile)
const statAsync = pify(stat)
const execFileAsync = pify(execFile)
const isWindows = process.platform === 'win32'
function pathExistsAsync(path: string) {
return statAsync(path)
.then(x => true)
.catch(falseOnEnoent)
}
function isDirectoryAsync(path: string) {
return statAsync(path)
.then(x => x.isDirectory())
.catch(falseOnEnoent)
}
/**
* @param version See if this version is greather than the second one
* @param operand Version to compare against
*/
function semverGt(version: string, operand: string) {
const [cMajor, cMinor, cPatch] = version.split('.').map(Number)
let [major, minor, patch] = operand.split('.').map(Number)
if (!minor) minor = 0
if (!patch) patch = 0
return (
cMajor > major ||
(cMajor === major && cMinor > minor) ||
(cMajor === major && cMinor === minor && cPatch > patch)
)
}
export {
dequote,
padRight,
semverGt,
bound,
isWindows,
rimrafAsync,
statAsync,
execFileAsync,
readFileAsync,
pathExistsAsync,
isDirectoryAsync,
writeFileAsync
}
+116
View File
@@ -0,0 +1,116 @@
import * as nexe from '../lib/nexe'
import { getUnBuiltReleases, getLatestGitRelease } from '../lib/releases'
import { runDockerBuild } from './docker'
import { getTarget, targetsEqual, NexeTarget } from '../lib/target'
import { pathExistsAsync, readFileAsync, execFileAsync, semverGt } from '../lib/util'
import got = require('got')
import { cpus } from 'os'
const env = process.env,
isPullRequest = env.BUILD_REASON === 'PullRequest',
isWindows = process.platform === 'win32',
isLinux = process.platform === 'linux',
buildHost = env.AGENT_JOBNAME || (isWindows && 'windows_2017_2015') || '',
isMac = process.platform === 'darwin',
headers = {
Authorization: 'token ' + env.GITHUB_TOKEN,
'User-Agent': 'nexe (https://www.npmjs.com/package/nexe)'
}
if (require.main === module) {
if (!isPullRequest) {
build().catch(x => {
console.error(x)
process.exit(1)
})
}
}
async function build() {
const releases = await getUnBuiltReleases({ headers })
if (!releases.length) {
return
}
const windowsBuild = releases.find(x => x.platform === 'windows'),
macBuild = releases.find(x => x.platform === 'mac'),
linux = releases.find(x => x.platform === 'linux'),
alpine = releases.find(x => x.platform === 'alpine')
let target: NexeTarget | undefined
if (env.NEXE_VERSION) target = getTarget(env.NEXE_VERSION)
else if (isWindows) target = windowsBuild
else if (isMac) target = macBuild
else if (isLinux) target = linux
if (buildHost.includes('alpine')) target = alpine
if (!target) {
return console.log('Nothing to build...')
}
if (isWindows && buildHost.includes('2017') && !semverGt(target.version, '9.99.99')) {
return console.log(`Not building ${target} on this host...`)
}
if (isWindows && buildHost.includes('2015') && semverGt(target.version, '9.99.99')) {
return console.log(`Not building ${target} on this host...`)
}
const output = isWindows ? './out.exe' : './out',
options = {
mangle: false,
build: true,
verbose: Boolean(env.NEXE_VERBOSE!),
target,
make: ['-j' + cpus().length],
output
}
console.log('Building: ' + target + ' on ' + buildHost)
const stop = keepalive()
if (
[/*'arm7l', 'arm6l', 'arm64', */ 'alpine'].includes(target.platform) &&
buildHost.includes('alpine')
) {
await runDockerBuild(target)
} else {
await nexe.compile(options)
}
stop()
if (await pathExistsAsync(output)) {
await assertNexeBinary(output)
const gitRelease = await getLatestGitRelease({ headers }),
unbuiltReleases = await getUnBuiltReleases({ headers })
if (!unbuiltReleases.some(x => targetsEqual(x, target!))) {
console.log(`${target} already uploaded.`)
process.exit(0)
return
}
await got(gitRelease.upload_url.split('{')[0], {
query: { name: target.toString() },
body: await readFileAsync(output),
headers: {
...headers,
'Content-Type': 'application/octet-stream'
}
}).catch(reason => {
console.log(reason && reason.response && reason.response.body)
throw reason
})
console.log(target + ' uploaded.')
process.exit(0)
}
}
function keepalive() {
const keepalive = setInterval(() => console.log('Building...'), 300 * 1000)
return () => clearInterval(keepalive)
}
function assertNexeBinary(file: string) {
return execFileAsync(file).catch(e => {
if (e && e.stack && e.stack.includes('Invalid Nexe binary')) {
return
}
throw e
})
}
+73
View File
@@ -0,0 +1,73 @@
import { NexeTarget, architectures } from '../lib/target'
import { writeFileAsync, readFileAsync } from '../lib/util'
import got = require('got')
import execa = require('execa')
import { appendFileSync } from 'fs'
function alpine(target: NexeTarget) {
return `
FROM ${target.arch === 'x64' ? '' : 'i386/'}alpine:3.4
RUN apk add --no-cache curl make gcc g++ binutils-gold python linux-headers paxctl libgcc libstdc++ git vim tar gzip wget
ENV NODE_VERSION=${target.version}
ENV NEXE_VERSION=latest
WORKDIR /
RUN curl -sSL https://nodejs.org/dist/v\${NODE_VERSION}/node-v\${NODE_VERSION}.tar.gz | tar -xz && \
mkdir /nexe_temp && mv /node-v\${NODE_VERSION} /nexe_temp/\${NODE_VERSION} && \
cd /nexe_temp/\${NODE_VERSION} && \
./configure --prefix=/usr --fully-static && \
make && make install && \
paxctl -cm /usr/bin/node
RUN rm /nexe_temp/\${NODE_VERSION}/out/Release/node && \
npm install -g nexe@\${NEXE_VERSION}
RUN echo "console.log('hello world')" >> index.js && \
nexe --build --no-mangle --temp /nexe_temp -c="--fully-static" -o out
`.trim()
}
function arm(target: NexeTarget) {
return `
FROM hypriot/rpi-node
ENV NEXE_VERSION=latest
WORKDIR /
RUN yarn global add nexe@\${NEXE_VERSION} && \
nexe --build --no-mangle -o out -t ${target.version}
`.trim()
}
export async function runDockerBuild(target: NexeTarget) {
//todo switch on alpine and arm
const dockerfile = alpine(target)
await writeFileAsync('Dockerfile', dockerfile)
const outFilename = 'nexe-docker-build-log.txt'
await writeFileAsync(outFilename, '')
let output: any = []
try {
output.push(await execa.shell(`docker build -t nexe-docker .`))
output.push(await execa.shell(`docker run -d --name nexe nexe-docker sh`))
output.push(await execa.shell(`docker cp nexe:/out out`))
output.push(await execa.shell(`docker rm nexe`))
} catch (e) {
console.log('Error running docker')
appendFileSync(outFilename, e.message)
} finally {
output.forEach((x: any) => {
appendFileSync(outFilename, x.stderr)
appendFileSync(outFilename, x.stdout)
})
await got(
`https://transfer.sh/${Math.random()
.toString(36)
.substring(2)}.txt`,
{
body: await readFileAsync(outFilename),
method: 'PUT'
}
)
.then(x => console.log('Posted docker log: ', x.body))
.catch(e => console.log('Error posting log', e))
}
}
+38
View File
@@ -0,0 +1,38 @@
import { writeFileSync, readFileSync } from 'fs'
/**
* post build step to insert code files into code files (naively).
* '{{replace:path/to/file}}' => "file contents"
* And the package.json version.
*/
//inject('plugins/nexe-daemon/lib/index.js')
cp('src/fs/package.json', 'lib/fs/package.json')
cp('src/fs/bootstrap.js', 'lib/fs/bootstrap.js')
cp('src/fs/README.md', 'lib/fs/README.md')
inject('lib/patches/third-party-main.js')
inject('lib/steps/shim.js')
inject('lib/options.js', JSON.stringify(require('../package.json').version))
function inject(filename: string, ...replacements: string[]) {
let contents = readFileSync(filename, 'utf8')
contents = contents.replace(/('{{(.*)}}')/g, (substring: string, ...matches: string[]) => {
if (!matches || !matches[1]) {
return substring
}
const [replace, file] = matches[1].split(':')
if (replace !== 'replace') {
return substring
}
console.log('Replacing: ', substring)
return replacements[+file] ? replacements[+file] : JSON.stringify(readFileSync(file, 'utf8'))
})
writeFileSync(filename, contents)
console.log(`Wrote: ${filename}`)
}
function cp(from: string, to: string) {
const file = readFileSync(from)
writeFileSync(to, file)
console.log('Copied: ', from, 'To: ', to)
}
+1
View File
@@ -0,0 +1 @@
module.exports = 'abc'
+1
View File
@@ -0,0 +1 @@
console.log('hello world', require('./dependency'))
+3
View File
@@ -0,0 +1,3 @@
{
"main": "entry.js"
}
View File
-13
View File
@@ -1,13 +0,0 @@
/**
* Test to verify if we support strict mode (flags)
*
* @author Jared Allard <jaredallard@outlook.com>
* @version 0.0.1
* @license MIT
**/
// "use strict"; w/o --use_strict
var isStrict = (function() { return !this; })();
console.log('test:strict-mode:status =', isStrict);
-15
View File
@@ -1,15 +0,0 @@
{
"name": "flags-test",
"nexe": {
"input": "./index.js",
"output": "test.nex",
"temp": "src",
"runtime": {
"framework": "node",
"version": "0.10.0",
"ignoreFlags": false,
"js-flags": "--use_strict",
"node-args": ""
}
}
}
-23
View File
@@ -1,23 +0,0 @@
"use strict";
var gulp = require('gulp');
var nexe = require('nexe');
gulp.task( "compile", ( callback ) => {
let options = {
input: "index.js",
output: "test.nex",
python: "python",
nodeTempDir: "./tmp",
nodeVersion: "latest",
flags: false,
framework: "nodejs"
};
nexe.compile( options, ( err ) => {
console.log( err );
callback( err );
} );
} );
gulp.task('default', ['compile']);
-1
View File
@@ -1 +0,0 @@
console.log('Hello, world!');
-15
View File
@@ -1,15 +0,0 @@
{
"name": "gulp-test-170",
"version": "0.0.1",
"description": "gulpfile.js issue #170",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Jared Allard <jaredallard@outlook.com>",
"license": "MIT",
"dependencies": {
"gulp": "^3.9.0",
"nexe": "^1.0.5"
}
}
+4
View File
@@ -0,0 +1,4 @@
./test/**/*.spec.ts
--check-leaks
--require ts-node/register
--recursive
+60
View File
@@ -0,0 +1,60 @@
import { normalizeOptions } from '../src/options'
import { expect } from 'chai'
import * as path from 'path'
const ext = process.platform === 'win32' ? '.exe' : ''
describe('options', () => {
describe('cwd', () => {
it('should use process.cwd() if nothing is provided', () => {
const options = normalizeOptions()
expect(options.cwd).to.equal(process.cwd())
})
it('should use the main module in a package directory (if not in a TTY)', () => {
const options = normalizeOptions()
if (!process.stdin.isTTY) {
expect(options.input).to.equal('[stdin]')
} else {
expect(options.input).to.equal(path.resolve('./index.js'))
}
})
it('should resolve relative paths for input', () => {
const options = normalizeOptions({ input: 'test/fixture/entry.js' })
expect(options.input).to.equal(path.resolve('./test/fixture/entry.js'))
})
it('should accept a module entry as input', () => {
const options = normalizeOptions({ input: 'test/fixture' })
expect(options.input).to.equal(path.resolve('./test/fixture/entry.js'))
})
it('should resolve pathed options against cwd', () => {
const cwd = path.join(process.cwd(), 'test/fixture')
const options = normalizeOptions({
cwd,
input: 'entry',
output: 'abc',
temp: './d'
})
expect(options.temp).to.equal(path.resolve(cwd, './d'))
expect(options.input).to.equal(path.resolve(cwd, 'entry.js'))
expect(options.output).to.equal(path.resolve(cwd, `abc${ext}`))
})
})
describe('output', () => {
it('should work', () => {
const options = normalizeOptions({
output: './some-output'
})
expect(options.output).to.equal(path.resolve(`./some-output${ext}`))
})
it('should default to the input file name if not index', () => {
const options = normalizeOptions({
input: './test/fixture'
})
expect(options.output).to.equal(path.resolve(`./entry${ext}`))
})
it('should default to the folder/project name if filename is index', () => {
const options = normalizeOptions({ cwd: './test/fixture2' })
expect(options.output).to.equal(path.resolve(`./test/fixture2/fixture2${ext}`))
})
})
})
+29
View File
@@ -0,0 +1,29 @@
import { padRight, isWindows } from '../src/util'
import { expect } from 'chai'
import chalk from 'chalk'
import { getTarget, NexeTarget } from '../src/target'
const b = chalk.blue
const arch = process.arch === 'ia32' ? 'x86' : process.arch
describe('Targets', () => {
;[
['win-ia32-6.11.2', 'windows-x86-6.11.2'],
[{ version: '6.11.2', platform: 'win', arch: 'ia32' }, 'windows-x86-6.11.2'],
['win32-x64-6.11.2', 'windows-x64-6.11.2'],
['win-amd64-6.11.2', 'windows-x64-6.11.2'],
['darwin-x64-v8.4.0', 'mac-x64-8.4.0'],
['macos-x64-v8.4.0', 'mac-x64-8.4.0'],
['static-x86-6.10.3', 'alpine-x86-6.10.3'],
['linux-x32', `linux-x86-${process.version.slice(1)}`],
['alpine-notsupported-6.10.3', `alpine-${arch}-6.10.3`],
['not-a-thing', getTarget(process).toString()]
].forEach(([input, expected]) => {
it(`should accept: ${padRight(JSON.stringify(input), 53)} ${b('->')} ${expected}`, () => {
expect(getTarget(input).toString()).to.equal(expected)
})
})
it('should stringify and toString', () => {
expect(JSON.stringify(getTarget(process))).to.equal(`"${getTarget(process)}"`)
})
})
+14
View File
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target":"es5",
"skipLibCheck": true,
"experimentalDecorators": true,
"lib": ["es2018"],
"module": "commonjs",
"outDir": "./lib",
"strict": true
},
"include":[
"src/**/*.ts"
]
}
+9
View File
@@ -0,0 +1,9 @@
{
"rulesDirectory": ["tslint-plugin-prettier", "tslint-config-prettier"],
"linterOptions": {
"exclude": ["**/node_modules/**/*"]
},
"rules": {
"prettier": [true, { "semi": false, "printWidth": 100, "singleQuote": true }]
}
}