From f949bf3040aba66d011e458e882a7feb7fd0ff09 Mon Sep 17 00:00:00 2001 From: calebboyd Date: Mon, 28 Aug 2017 00:06:46 -0500 Subject: [PATCH] feat: initial build --- .editorconfig | 3 +- .vscode/settings.json | 14 +- CHANGELOG.md | 6 +- README.md | 9 +- appveyor.yml | 22 +- package-lock.json | 935 ++++++++++++++++++++++++++++---- package.json | 11 +- src/artifacts.ts | 62 --- src/bundling/fuse.ts | 24 +- src/compiler.ts | 29 +- src/logger.ts | 7 +- src/nexe.ts | 30 +- src/options.ts | 96 ++-- src/patches/third-party-main.ts | 8 +- src/steps/artifacts.ts | 70 +++ src/{ => steps}/cli.ts | 18 +- src/{ => steps}/download.ts | 6 +- src/{ => steps}/resource.ts | 5 +- src/target.ts | 86 +++ src/types.d.ts | 22 +- src/util.ts | 43 +- tasks/build.ts | 88 ++- tasks/bump.ts | 0 tasks/ci.ts | 46 ++ tasks/releases.ts | 62 +++ tsconfig.json | 3 +- 26 files changed, 1355 insertions(+), 350 deletions(-) delete mode 100644 src/artifacts.ts create mode 100644 src/steps/artifacts.ts rename src/{ => steps}/cli.ts (82%) rename src/{ => steps}/download.ts (91%) rename src/{ => steps}/resource.ts (87%) create mode 100644 src/target.ts create mode 100644 tasks/bump.ts create mode 100644 tasks/ci.ts create mode 100644 tasks/releases.ts diff --git a/.editorconfig b/.editorconfig index 0a252e4..9e42605 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,5 +4,6 @@ end_of_line = lf charset = utf-8 indent_style = space indent_size = 2 -trim_trailing_whitespace=true +#https://github.com/Microsoft/vscode/issues/23983#event-1087502108 +#trim_trailing_whitespace = true insert_final_newline = true diff --git a/.vscode/settings.json b/.vscode/settings.json index 29e36fa..cc468c9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,17 @@ "typescript.tsdk": "./node_modules/typescript/lib", "prettier.semi": false, "prettier.singleQuote": true, - "prettier.typescriptEnable": ["typescript"] + "prettier.typescriptEnable": ["typescript"], + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true + }, + "search.exclude": { + "lib/**/*": true, + "**/node_modules": true, + "**/bower_components": true + } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 376e20d..cb35661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,9 @@ Nexe 2.0 is a rewrite to enable some new features. These include: * Quick Builds! * Userland build patches - * New Resource access - * stdin interfaces - * Opt-in bundling + * Resource storage/access rewrite + * stdin interface + * Optional, pluggable bundling ### Breaking Changes * New options -- Please see the [readme](README.md#options) diff --git a/README.md b/README.md index 5a7e046..409df6d 100644 --- a/README.md +++ b/README.md @@ -112,11 +112,10 @@ nexe.compile({ - Example: `['--with-dtrace', '--dest-cpu=x64']` - default: `[]` - #### `make: Array` - - Array of arguments for the node build make step - - default: `[]` - - #### `vcBuild: Array` - - Array of arguments for the node build step on windows - - default: `['nosign', 'release']` + - Array of arguments for the node build make step, on windows this step recieves optiosn for vcBuild.bat + - default: `[]` or `['nosign', 'release']` for non windows systems + - #### `make: Array` + - Alias for `make` option - #### `snapshot: string` - path to a file to be used as the warmup snapshot for the build - default: `null` diff --git a/appveyor.yml b/appveyor.yml index a43a011..75df911 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,25 +1,21 @@ environment: - node_version: "6" - + CIRCLE_TOKEN: + secure: TQKcepXmTzCNt4+tUBtYpwEQkdSPwFr9A8ugtoMhNbKu/SW1WM1EpjEu6kYXxpQ4 + TRAVIS_TOKEN: + secure: IwsFkKeSwogeIs7gbEhpIkRbrziWdGq0eCSMkAVCgBY= + GITHUB_TOKEN: + secure: Sx2cy8b/djm/3wSYMSKK0fxbZ0mnFI09rLCHXqjjzwhunfEkug7xILEl14wVrHUS branches: except: - master - install: - - ps: Install-Product node $env:node_version + - ps: Install-Product node 6 - npm i - build: off test: off - -before_build: - - npm run log-node-version - build_script: - - npm run task-build-windows - + - npm run nexe-build artifacts: - - path: .nexe\6.11.2\Release\node.exe - + - path: .nexe\**\node.exe cache: - node_modules diff --git a/package-lock.json b/package-lock.json index 749539c..e49175b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,6 +68,12 @@ "@types/node": "8.0.25" } }, + "@types/pify": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/@types/pify/-/pify-0.0.28.tgz", + "integrity": "sha1-aCtsM6HQV0i4bwKb2rb2Kqc/9QY=", + "dev": true + }, "@types/rimraf": { "version": "0.0.28", "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-0.0.28.tgz", @@ -318,11 +324,6 @@ "readable-stream": "2.3.3" } }, - "bluebird": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", - "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" - }, "boom": { "version": "2.10.1", "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", @@ -411,6 +412,7 @@ "requires": { "anymatch": "1.3.2", "async-each": "1.0.1", + "fsevents": "1.1.2", "glob-parent": "2.0.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -419,11 +421,6 @@ "readdirp": "2.1.0" } }, - "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" - }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -744,14 +741,6 @@ "once": "1.4.0" } }, - "errno": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", - "requires": { - "prr": "0.0.0" - } - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1024,16 +1013,6 @@ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.16" - } - }, "forwarded": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", @@ -1058,6 +1037,791 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", + "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==", + "optional": true, + "requires": { + "nan": "2.6.2", + "node-pre-gyp": "0.6.36" + }, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.2.9" + } + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "optional": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "bundled": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "optional": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "optional": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.0" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "optional": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true, + "requires": { + "mime-db": "1.27.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.36", + "bundled": true, + "optional": true, + "requires": { + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.0", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "bundled": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.0.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" + } + }, + "rimraf": { + "version": "2.6.1", + "bundled": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "optional": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "optional": true, + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.2.9", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "optional": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + } + } + }, "fuse-box": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/fuse-box/-/fuse-box-2.2.2.tgz", @@ -1732,15 +2496,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "requires": { - "errno": "0.1.4", - "readable-stream": "2.3.3" - } - }, "merge": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", @@ -1817,22 +2572,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, - "minipass": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.1.tgz", - "integrity": "sha512-u1aUllxPJUI07cOqzR7reGmQxmCqlH88uIIsf6XZFEWgw7gXKpJdR+5R9Y3KEDmWYkdIz9wXZs3C0jOPxejk/Q==", - "requires": { - "yallist": "3.0.2" - } - }, - "minizlib": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.3.tgz", - "integrity": "sha1-1cGr93vhVGGZUuJTM27Mq5sqMvU=", - "requires": { - "minipass": "2.2.1" - } - }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -1922,6 +2661,12 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, + "nan": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", + "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=", + "optional": true + }, "nanoseconds": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/nanoseconds/-/nanoseconds-0.1.0.tgz", @@ -1932,22 +2677,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, - "nigel": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/nigel/-/nigel-2.0.2.tgz", - "integrity": "sha1-k6GGb7DFLYc5CqdeKxYfS1x15bE=", - "requires": { - "hoek": "4.2.0", - "vise": "2.0.2" - }, - "dependencies": { - "hoek": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" - } - } - }, "node-fetch": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz", @@ -2162,6 +2891,11 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" }, + "pifi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pifi/-/pifi-1.0.0.tgz", + "integrity": "sha1-FY93C7X2JyTdYSuS/CwsWaU9fi8=" + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -2272,11 +3006,6 @@ "ipaddr.js": "1.4.0" } }, - "prr": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", - "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=" - }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -2428,6 +3157,16 @@ "uuid": "3.1.0" }, "dependencies": { + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.16" + } + }, "qs": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", @@ -2685,18 +3424,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, - "tar": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-3.2.1.tgz", - "integrity": "sha512-ZSzds1E0IqutvMU8HxjMaU8eB7urw2fGwTq88ukDOVuUIh0656l7/P7LiVPxhO5kS4flcRJQk8USG+cghQbTUQ==", - "requires": { - "chownr": "1.0.1", - "minipass": "2.2.1", - "minizlib": "1.0.3", - "mkdirp": "0.5.1", - "yallist": "3.0.2" - } - }, "tar-stream": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz", @@ -2838,6 +3565,22 @@ "integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=", "dev": true }, + "uglify-js": { + "version": "3.0.28", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.0.28.tgz", + "integrity": "sha512-0h/qGay016GG2lVav3Kz174F3T2Vjlz2v6HCt+WDQpoXfco0hWwF5gHK9yh88mUYvIC+N7Z8NT8WpjSp1yoqGA==", + "requires": { + "commander": "2.11.0", + "source-map": "0.5.7" + }, + "dependencies": { + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" + } + } + }, "ultron": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", @@ -2927,21 +3670,6 @@ } } }, - "vise": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/vise/-/vise-2.0.2.tgz", - "integrity": "sha1-awjo+0y3bjpQzW3Q7DczjoEaDTk=", - "requires": { - "hoek": "4.2.0" - }, - "dependencies": { - "hoek": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" - } - } - }, "watch": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/watch/-/watch-1.0.2.tgz", @@ -2975,11 +3703,6 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, - "yallist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", - "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" - }, "yauzl": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz", diff --git a/package.json b/package.json index 14701b5..5b8a54d 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,7 @@ "Caleb Boyd " ], "scripts": { - "task-build": "ts-node tasks/build", - "log-node-version": "node -e \"console.log([process.platform, process.arch, process.version.slice(1)].join('-'))\"", + "nexe-build": "ts-node tasks/build", "prebuild": "rimraf lib && npm run lint", "prepublish": "npm run build", "lint": "prettier --parser typescript --no-semi --print-width 100 --single-quote --write \"src/**/*.ts\"", @@ -33,18 +32,16 @@ }, "dependencies": { "app-builder": "^5.1.0", - "bluebird": "^3.5.0", "chalk": "^1.1.3", "download": "^6.2.0", "fuse-box": "^2.2.1", "globby": "^6.1.0", - "memory-fs": "^0.4.1", "minimist": "^1.2.0", "mkdirp": "^0.5.1", - "nigel": "^2.0.2", "ora": "^1.2.0", + "pifi": "1.0.0", "rimraf": "^2.6.1", - "tar": "^3.1.3" + "uglify-js": "3.0.28" }, "devDependencies": { "@types/bluebird": "^3.5.8", @@ -53,7 +50,9 @@ "@types/minimist": "^1.2.0", "@types/mkdirp": "^0.3.29", "@types/ora": "^0.3.31", + "@types/pify": "0.0.28", "@types/rimraf": "0.0.28", + "got": "^7.1.0", "mocha": "^3.2.0", "prettier": "^1.4.4", "ts-node": "3.3.0", diff --git a/src/artifacts.ts b/src/artifacts.ts deleted file mode 100644 index 83f9120..0000000 --- a/src/artifacts.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { join, dirname } from 'path' -import { readdir, unlink } from 'fs' -import { readFileAsync, writeFileAsync, isDirectoryAsync } from './util' -import { promisify, map } from 'bluebird' -import * as mkdirp from 'mkdirp' -import { NexeCompiler } from './compiler' - -const mkdirpAsync = promisify(mkdirp) -const unlinkAsync = (promisify(unlink) as any) as (path: string) => PromiseLike -const readdirAsync = promisify(readdir) - -function readDirAsync(dir: string): PromiseLike { - return readdirAsync(dir) - .map((file: string) => { - const path = join(dir, file) - return isDirectoryAsync(path).then((x: boolean) => (x ? readDirAsync(path) : path as any)) - }) - .reduce((a: string[], b: string[] | string) => a.concat(b), []) -} - -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) { - const { src } = compiler - const temp = join(src, 'nexe') - await mkdirpAsync(temp) - const tmpFiles = await readDirAsync(temp) - - await map(tmpFiles, async path => { - return compiler.writeFileAsync(path.replace(temp, ''), await readFileAsync(path, 'utf-8')) - }) - - await next() - - await map(tmpFiles, x => unlinkAsync(x)) - return map(compiler.files, 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) - }) -} diff --git a/src/bundling/fuse.ts b/src/bundling/fuse.ts index 67f12e6..0e5fc79 100644 --- a/src/bundling/fuse.ts +++ b/src/bundling/fuse.ts @@ -1,9 +1,18 @@ import { NexeCompiler } from '../compiler' -import { FuseBox, JSONPlugin, CSSPlugin, HTMLPlugin, SourceMapPlainJsPlugin } from 'fuse-box' +import { FuseBox, JSONPlugin, CSSPlugin, HTMLPlugin, QuantumPlugin } from 'fuse-box' //import NativeModulePlugin from './fuse-native-module-plugin' -function bundleProducer(filename: string, name: string) { - console.error(process.cwd()) +function bundleProducer(filename: string, options: { name: string; minify: any }) { + const plugins: any = [JSONPlugin(), CSSPlugin(), HTMLPlugin()] + if (options.minify) { + plugins.push( + QuantumPlugin({ + target: 'server', + uglify: true, + bakeApiIntoBundle: options.name + }) + ) + } const fuse = FuseBox.init({ cache: false, log: Boolean(process.env.NEXE_BUNDLE_DEBUG) || false, @@ -12,9 +21,9 @@ function bundleProducer(filename: string, name: string) { writeBundles: false, output: '$name.js', target: 'server', - plugins: [JSONPlugin(), CSSPlugin(), HTMLPlugin(), SourceMapPlainJsPlugin()] + plugins }) - fuse.bundle(name).instructions(`> ${filename}`) + fuse.bundle(options.name).instructions(`> ${filename}`) return fuse.run().then(x => { let output = '' x.bundles.forEach(y => (output = y.context.output.lastPrimaryOutput.content!.toString())) @@ -32,6 +41,9 @@ export default async function bundle(compiler: NexeCompiler, next: any) { producer = require(compiler.options.bundle).bundleProducer } - compiler.input = await producer(compiler.options.input, compiler.options.name) + compiler.input = await producer(compiler.options.input, { + name: compiler.options.name, + minify: compiler.options.compress + }) return next() } diff --git a/src/compiler.ts b/src/compiler.ts index 7401067..14ee089 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1,14 +1,13 @@ import { normalize, join } from 'path' -import * as Bluebird from 'bluebird' import { Buffer } from 'buffer' import { createHash } from 'crypto' import { createReadStream } from 'fs' import { Readable } from 'stream' import { spawn } from 'child_process' -import { Stream as Needle } from 'nigel' import { Logger } from './logger' import { readFileAsync, writeFileAsync, pathExistsAsync, dequote, isWindows } from './util' -import { NexeOptions } from './options' +import { NexeOptions, nexeVersion } from './options' +import { NexeTarget } from './target' const isBsd = Boolean(~process.platform.indexOf('bsd')) const make = isWindows ? 'vcbuild.bat' : isBsd ? 'gmake' : 'make' @@ -51,7 +50,7 @@ export class NexeCompiler { constructor(public options: NexeOptions) { const { python } = (this.options = options) - + this.log.step('nexe ' + nexeVersion, 'info') if (python) { if (isWindows) { this.env.PATH = '"' + dequote(normalize(python)) + '";' + this.env.PATH @@ -67,7 +66,10 @@ export class NexeCompiler { cachedFile = { absPath, filename: file, - contents: await readFileAsync(absPath, 'utf-8').catch({ code: 'ENOENT' }, () => '') + contents: await readFileAsync(absPath, 'utf-8').catch(x => { + if (x.code === 'ENOENT') return '' + throw x + }) } this.files.push(cachedFile) } @@ -84,21 +86,21 @@ export class NexeCompiler { } } - quit(code = 0) { + quit() { const time = Date.now() - this.start this.log.write(`Finsihed in ${time / 1000}s`) - return this.log.flush().then(x => process.exit(code)) + return this.log.flush() } - private _getNodeExecutableLocation(target?: string | null) { + private _getNodeExecutableLocation(target?: NexeTarget) { if (target) { - return join(this.options.temp, target) + return join(this.options.temp, target.toString()) } return this.nodeSrcBinPath } private _runBuildCommandAsync(command: string, args: string[]) { - return new Bluebird((resolve, reject) => { + return new Promise((resolve, reject) => { spawn(command, args, { cwd: this.src, env: this.env, @@ -123,7 +125,7 @@ export class NexeCompiler { : '...'}` ) await this._configureAsync() - const buildOptions = isWindows ? this.options.vcBuild : this.options.make + const buildOptions = this.options.make this.compileStep.log( `Compiling Node${buildOptions.length ? ' with arguments: ' + buildOptions : '...'}` ) @@ -152,16 +154,13 @@ export class NexeCompiler { return `process.__nexe=${JSON.stringify(header)};` } - async compileAsync() { + async compileAsync(target: NexeTarget) { const step = (this.compileStep = this.log.step('Compiling result')) - let target = this.options.targets.slice().shift() const location = this._getNodeExecutableLocation(target) let binary = (await pathExistsAsync(location)) ? createReadStream(location) : null const header = this._generateHeader() - step.log(`Scanning existing binary...`) if (target && !binary) { - //throw new Error('\nNot Implemented, use --build during beta\n') binary = await this._fetchPrebuiltBinaryAsync() } diff --git a/src/logger.ts b/src/logger.ts index 532237a..2f7a159 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -46,14 +46,17 @@ export class Logger { this.ora.color = color } - step(text: string): LogStep { + step(text: string, method: string = 'succeed'): LogStep { if (this.silent) { return { modify() {}, log() {} } } if (!this.ora.id) { this.ora.start().text = text + if (method !== 'succeed') { + this.ora[method]() + } } else { - this.ora.succeed().text = text + this.ora[method]().text = text this.ora.start() } diff --git a/src/nexe.ts b/src/nexe.ts index ed12d74..fc6182e 100644 --- a/src/nexe.ts +++ b/src/nexe.ts @@ -1,18 +1,18 @@ import { compose, PromiseConfig, Middleware } from 'app-builder' -import resource from './resource' +import resource from './steps/resource' import { NexeCompiler } from './compiler' -import { argv, normalizeOptionsAsync, NexeOptions } from './options' -import cli from './cli' +import { argv, normalizeOptionsAsync, NexeOptions, NexePatch } from './options' +import cli from './steps/cli' import bundle from './bundling/fuse' -import download from './download' -import artifacts from './artifacts' +import download from './steps/download' +import artifacts from './steps/artifacts' import patches from './patches' import { rimrafAsync } from './util' -import * as Bluebird from 'bluebird' -PromiseConfig.constructor = Bluebird - -async function compile(compilerOptions: NexeOptions, callback?: (err: Error | null) => void) { +async function compile( + compilerOptions?: Partial, + callback?: (err: Error | null) => void +) { const options = await normalizeOptionsAsync(compilerOptions) const compiler = new NexeCompiler(options) const build = compiler.options.build @@ -25,9 +25,17 @@ async function compile(compilerOptions: NexeOptions, callback?: (err: Error | nu return compiler.quit() } - const buildSteps = build ? [download, artifacts, ...patches, ...options.patches] : [] + const buildSteps = build + ? [download, artifacts, ...patches, ...(options.patches as NexePatch[])] + : [] const nexe = compose(resource, bundle, cli, buildSteps) - return nexe(compiler).asCallback(callback) + return callback + ? void nexe(compiler).then(() => callback && callback(null)).catch((e: Error) => { + if (callback) { + callback(e) + } else throw e + }) + : nexe(compiler) } export { argv, compile } diff --git a/src/options.ts b/src/options.ts index c18bbfc..eb02eb3 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,72 +1,49 @@ import * as parseArgv from 'minimist' import { NexeCompiler } from './compiler' -import { basename, extname, join } from 'path' -import * as Bluebird from 'bluebird' +import { isWindows } from './util' +import { basename, extname, join, isAbsolute } from 'path' +import { getTarget, NexeTarget } from './target' import { EOL } from 'os' +export const nexeVersion = '2.0.0-rc.1' + export interface NexePatch { (compiler: NexeCompiler, next: () => Promise): Promise } export interface NexeOptions { build: boolean - /** - * Entrypoint filepath - */ input: string output: string - targets: string[] - /** - * Build name, Used for executable, and stacktraces - */ + compress: boolean + targets: (string | NexeTarget)[] name: string - /** - * The node version to be built - */ version: string - python?: string - /** - * Node flags e.g. "--expose-gc" baked into the executable - */ flags: string[] - /** - * Pass configuration options to node build configure script - */ configure: string[] - /** - * Pass make options to node make script - */ - make: string[] vcBuild: string[] + make: string[] snapshot?: string - /** - * Array of glob strings describing resources to pull into the bundle - */ resources: string[] - /** - * Temporary directory where nexe artifacts will be cached - * TODO dot folder in home dir - */ temp: string - ico?: string - sourceMaps?: boolean rc: { [key: string]: string } - /** - * Causes nexe to remove all temporary files for current configuration - */ - clean: boolean enableNodeCli: boolean - sourceUrl?: string bundle: boolean | string + patches: (string | NexePatch)[] + empty: boolean + sourceUrl?: string + python?: string loglevel: 'info' | 'silent' | 'verbose' silent?: boolean verbose?: boolean info?: boolean - patches: NexePatch[] - - empty: boolean + ico?: string warmup?: string - downloadOptions?: any + clean?: boolean + /** + * Api Only + */ + downloadOptions: any } function padRight(str: string, l: number) { @@ -80,10 +57,11 @@ const defaults = { configure: [], make: [], targets: [], - vcBuild: ['nosign', 'release', process.arch], + vcBuild: isWindows ? ['nosign', 'release', process.arch] : [], enableNodeCli: false, + compress: false, + build: false, bundle: true, - build: true, patches: [] } const alias = { @@ -98,9 +76,7 @@ const alias = { f: 'flag', c: 'configure', m: 'make', - vc: 'vcBuild', s: 'snapshot', - cli: 'enableNodeCli', h: 'help', l: 'loglevel' } @@ -117,9 +93,8 @@ nexe --help CLI OPTIONS -v --version =${padRight(process.version.slice(1), 23)}-- node version -p --python =/path/to/python2 -- python executable -f --flag ="--expose-gc" -- *v8 flags to include during compilation - -c --configure ="--with-dtrace" -- *pass arguments to the configure command - -m --make ="--loglevel" -- *pass arguments to the make command - -vc --vcBuild =x64 -- *pass arguments to vcbuild.bat + -c --configure ="--with-dtrace" -- *pass arguments to the configure step + -m --make ="--loglevel" -- *pass arguments to the make/build step -s --snapshot =/path/to/snapshot -- build with warmup snapshot -r --resource =./paths/**/* -- *embed file bytes within the binary --bundle =./path/to/config -- pass a module path that exports nexeBundle @@ -182,20 +157,20 @@ function extractName(options: NexeOptions) { return name.replace(/\.exe$/, '') } -function normalizeOptionsAsync(input: Partial) { - if (argv.help || argv._.some((x: string) => x === 'version')) { - process.stderr.write(argv.help ? help : '2.0.0-rc.1' + EOL, () => process.exit(0)) +function normalizeOptionsAsync(input?: Partial): Promise { + if (argv.help || argv._.some((x: string) => x === 'version') || argv.version === true) { + return new Promise(() => { + process.stderr.write(argv.help ? help : nexeVersion + EOL, () => process.exit(0)) + }) } - const options = Object.assign({}, defaults, input) as NexeOptions const opts = options as any - delete opts._ + options.loglevel = extractLogLevel(options) options.name = extractName(options) options.flags = flattenFilter(opts.flag, options.flags) options.targets = flattenFilter(opts.target, options.targets) - options.make = flattenFilter(options.make) - options.vcBuild = flattenFilter(options.vcBuild) + options.make = flattenFilter(options.vcBuild, options.make) options.configure = flattenFilter(options.configure) options.resources = flattenFilter(opts.resource, options.resources) options.rc = options.rc || extractCliMap(/^rc-.*/, options) @@ -204,10 +179,17 @@ function normalizeOptionsAsync(input: Partial) { options.targets = [] options.build = true } else if (!options.targets.length) { - const defaultTarget = [process.platform, process.arch, options.version].join('-') - options.targets = [defaultTarget] + options.targets = [getTarget()] } + options.targets = options.targets.map(getTarget) + options.patches = options.patches.map(x => { + if (typeof x === 'string') { + return require(x).default + } + return x + }) + Object.keys(alias).filter(k => k !== 'rc').forEach(x => delete opts[x]) return Promise.resolve(options) diff --git a/src/patches/third-party-main.ts b/src/patches/third-party-main.ts index 8b6cbf2..1bdb3f9 100644 --- a/src/patches/third-party-main.ts +++ b/src/patches/third-party-main.ts @@ -3,11 +3,6 @@ import { readFileSync } from 'fs' import { join } from 'path' export default async function main(compiler: NexeCompiler, next: () => Promise) { - let sourceMapSupport = '' - if (true /*compiler.options.sourceMaps*/) { - sourceMapSupport = readFileSync(join(__dirname, '../../source-map-support.js')).toString() - } - await compiler.setFileContentsAsync( 'lib/_third_party_main.js', ` @@ -21,7 +16,7 @@ const footer = Buffer.from(Array(32)) fs.readSync(fd, footer, 0, 32, size - 32) if (!footer.slice(0, 16).equals(Buffer.from(''))) { - throw new Error('Invalid Nexe binary') + throw 'Invalid Nexe binary' } const contentSize = footer.readDoubleLE(16) @@ -102,7 +97,6 @@ if (resourceSize) { const contentBuffer = Buffer.from(Array(contentSize)); fs.readSync(fd, contentBuffer, 0, contentSize, contentStart); fs.closeSync(fd); -${sourceMapSupport} const Module = require('module'); process.mainModule = new Module(process.execPath, null); process.mainModule.loaded = true; diff --git a/src/steps/artifacts.ts b/src/steps/artifacts.ts new file mode 100644 index 0000000..11ccbdc --- /dev/null +++ b/src/steps/artifacts.ts @@ -0,0 +1,70 @@ +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 { + 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) { + 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) + }) + ) +} diff --git a/src/cli.ts b/src/steps/cli.ts similarity index 82% rename from src/cli.ts rename to src/steps/cli.ts index ec48a76..f8803fd 100644 --- a/src/cli.ts +++ b/src/steps/cli.ts @@ -1,12 +1,12 @@ import { normalize } from 'path' -import * as Bluebird from 'bluebird' import { Readable } from 'stream' import { createWriteStream, chmodSync } from 'fs' -import { readFileAsync, dequote, isWindows } from './util' -import { NexeCompiler } from './compiler' +import { readFileAsync, dequote, isWindows } from '../util' +import { NexeCompiler } from '../compiler' +import { NexeTarget } from '../target' function readStreamAsync(stream: NodeJS.ReadableStream): PromiseLike { - return new Bluebird(resolve => { + return new Promise(resolve => { let input = '' stream.setEncoding('utf-8') stream.on('data', (x: string) => { @@ -56,10 +56,14 @@ export default async function cli(compiler: NexeCompiler, next: () => Promise { + const target = compiler.options.targets.shift() as NexeTarget + const deliverable = await compiler.compileAsync(target) + + return new Promise((resolve, reject) => { const step = log.step('Writing result to file') deliverable .pipe(createWriteStream(normalize(compiler.output!))) diff --git a/src/download.ts b/src/steps/download.ts similarity index 91% rename from src/download.ts rename to src/steps/download.ts index 3ff024b..3484e69 100644 --- a/src/download.ts +++ b/src/steps/download.ts @@ -1,8 +1,8 @@ import download = require('download') -import { isDirectoryAsync } from './util' -import { LogStep } from './logger' +import { isDirectoryAsync } from '../util' +import { LogStep } from '../logger' import { IncomingMessage } from 'http' -import { NexeCompiler } from './compiler' +import { NexeCompiler } from '../compiler' function fetchNodeSourceAsync(cwd: string, url: string, step: LogStep, options = {}) { const setText = (p: number) => step.modify(`Downloading Node: ${p.toFixed()}%...`) diff --git a/src/resource.ts b/src/steps/resource.ts similarity index 87% rename from src/resource.ts rename to src/steps/resource.ts index 7367294..5cdb88a 100644 --- a/src/resource.ts +++ b/src/steps/resource.ts @@ -1,8 +1,7 @@ -import { each } from 'bluebird' -import { readFileAsync, isDirectoryAsync } from './util' +import { readFileAsync, isDirectoryAsync, each } from '../util' import { Buffer } from 'buffer' import * as globs from 'globby' -import { NexeCompiler } from './compiler' +import { NexeCompiler } from '../compiler' export default async function resource(compiler: NexeCompiler, next: () => Promise) { const resources = compiler.resources diff --git a/src/target.ts b/src/target.ts new file mode 100644 index 0000000..a4cf4e9 --- /dev/null +++ b/src/target.ts @@ -0,0 +1,86 @@ +export type NodePlatform = 'windows' | 'mac' | 'alpine' | 'linux' +export type NodeArch = 'x86' | 'x64' + +const platforms: NodePlatform[] = ['windows', 'mac', 'alpine', 'linux'], + architectures: NodeArch[] = ['x86', 'x64'] + +export { platforms, architectures } + +export interface NexeTarget { + version: string + platform: NodePlatform + arch: NodeArch +} + +//TODO bsd +const prettyPlatform: { [key: string]: NodePlatform } = { + win32: 'windows', + windows: 'windows', + darwin: 'mac', + macos: 'mac', + mac: 'mac', + linux: 'linux', + static: 'alpine', + alpine: 'alpine' +} + +//TODO arm +const prettyArch: { [key: string]: NodeArch } = { + x86: 'x86', + ia32: 'x86', + x32: 'x86', + x64: 'x64' +} + +function isVersion(x: string) { + if (!x) { + return false + } + return /\d/.test(x.replace(/v|\./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 | NexeTarget = ''): NexeTarget { + let 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 + } + if (isPlatform(x)) { + platform = prettyPlatform[x] + } + if (isArch(x)) { + arch = prettyArch[x] + } + }) + + return new Target(arch, platform, version) +} diff --git a/src/types.d.ts b/src/types.d.ts index 574c27a..43a6673 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,23 +1,7 @@ -declare module 'nigel' { - import { Writable } from 'stream' - interface Needle { - value: Buffer - lastPos: number - last: number - length: number - badCharShift: Buffer - } - - export function compile(needle: Buffer): Needle - export function horspool(haystack: Buffer, needle: Needle, start: number): number - export function all(haystack: Buffer, needle: Needle, pos: number): number[] - export class Stream extends Writable { - constructor(needle: Buffer) - needle(needle: Buffer): void - flush(): void - } +declare module 'got' { + function got(url: string, options?: any): Promise<{ body: string }> + export = got } - declare module 'download' { import { Duplex } from 'stream' interface DownloadOptions { diff --git a/src/util.ts b/src/util.ts index 525571a..d5364df 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,9 +1,26 @@ import { readFile, writeFile, stat } from 'fs' -import * as Bluebird from 'bluebird' +import { execFile } from 'child_process' +import pify = require('pify') import rimraf = require('rimraf') -const { promisify } = Bluebird -const rimrafAsync = (promisify(rimraf) as any) as (path: string) => Bluebird +const rimrafAsync = pify(rimraf) + +export async function each( + list: T[] | Promise, + action: (item: T, index: number, list: T[]) => Promise +) { + const l = await list + for (let i = 0; i < l.length; i++) { + await action(l[i], i, l) + } +} + +function falseOnEnoent(e: any) { + if (e.code === 'ENOENT') { + return false + } + throw e +} function dequote(input: string) { input = input.trim() @@ -16,30 +33,30 @@ function dequote(input: string) { } export interface ReadFileAsync { - (path: string): Bluebird - (path: string, encoding: string): Bluebird + (path: string): Promise + (path: string, encoding: string): Promise } -const readFileAsync = (promisify(readFile) as any) as ReadFileAsync -const writeFileAsync = (promisify(writeFile) as any) as ( - path: string, - contents: string | Buffer -) => Promise -const statAsync = promisify(stat) +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({ code: 'ENOENT' }, () => false) + return statAsync(path).then(x => true).catch(falseOnEnoent) } function isDirectoryAsync(path: string) { - return statAsync(path).then(x => x.isDirectory()).catch({ code: 'ENOENT' }, () => false) + return statAsync(path).then(x => x.isDirectory()).catch(falseOnEnoent) } export { dequote, isWindows, rimrafAsync, + statAsync, + execFileAsync, readFileAsync, pathExistsAsync, isDirectoryAsync, diff --git a/tasks/build.ts b/tasks/build.ts index aa27a1c..fa5e651 100644 --- a/tasks/build.ts +++ b/tasks/build.ts @@ -1,19 +1,91 @@ -import { compile } from '../' +import { compile } from '../src/nexe' +import { + getUnBuiltReleases, + getLatestGitRelease +} from './releases' +import * as ci from './ci' +import { getTarget } from '../src/target' +import { pathExistsAsync, statAsync, readFileAsync, execFileAsync } from '../src/util' import got = require('got') const env = process.env, branchName = env.CIRCLE_BRANCH || env.APPVEYOR_REPO_BRANCH, - isScheduled = Boolean(env.APPVEYOR_SCHEDULED_BUILD), - isWindows = branchName.includes('win32') && Boolean(env.APPVEYOR), + isScheduled = Boolean(env.APPVEYOR_SCHEDULED_BUILD || env.NEXE_TRIGGERED), + isLinux = Boolean(env.TRAVIS), + isWindows = Boolean(env.APPVEYOR), + isMac = Boolean(env.CIRCLECI), isPullRequest = Boolean(env.CIRCLE_PR_NUMBER) || Boolean(env.APPVEYOR_PULL_REQUEST_NUMBER) async function build () { if (isScheduled) { - const releases = await + const releases = await getUnBuiltReleases() + if (!releases.length) { + return + } + const windowsBuild = releases.find(x => x.platform === 'windows') + const macBuild = releases.find(x => x.platform === 'mac') + const linuxOrAlpine = releases.find(x => x.platform === 'linux' || x.platform === 'alpine') + + if (linuxOrAlpine) { + await ci.triggerDockerBuild(linuxOrAlpine) + } + if (macBuild) { + await ci.triggerMacBuild(macBuild) + } + if (windowsBuild) { + await ci.triggerWindowsBuild(windowsBuild) + } + } + + if (env.NEXE_VERSION) { + const + target = getTarget(env.NEXE_VERSION), + output = isWindows ? './out.exe' : './out', + options = { + empty: true, + build: true, + make: [target.arch], + version: target.version, + output + } + if (isWindows) { + await compile(options) + } + + if (isMac) { + await compile(options) + } + + if (isLinux) {} + + if (await pathExistsAsync(output)) { + await assertNexeBinary(output) + const gitRelease = await getLatestGitRelease() + await got(gitRelease.upload_url.split('{')[0], { + query: { name: target.toString() }, + body: await readFileAsync(output), + headers: { + 'Authorization': 'token ' + env.GITHUB_TOKEN, + 'Content-Type': 'application/octet-stream' + } + }) + console.log(target + ' uploaded.') + } } } -build().catch(x => { - console.error(x) - process.exit(1) -}) +function assertNexeBinary (file: string) { + return execFileAsync(file).catch(e => { + if (e && e.stack && e.stack.includes('Invalid Nexe binary')) { + return + } + throw e + }) +} + +if (require.main === module) { + build().catch(x => { + console.error(x) + process.exit(1) + }) +} diff --git a/tasks/bump.ts b/tasks/bump.ts new file mode 100644 index 0000000..e69de29 diff --git a/tasks/ci.ts b/tasks/ci.ts new file mode 100644 index 0000000..010212d --- /dev/null +++ b/tasks/ci.ts @@ -0,0 +1,46 @@ +import { NexeTarget } from './releases' +import got = require('got') +import * as assert from 'assert' +const { env } = process + +export function triggerDockerBuild(release: NexeTarget) { + return Promise.resolve() + // assert.ok(env.TRAVIS_TOKEN) + // const travis = `https://api.travis-ci.org/repo/nexe/nexe/requests` + // return got(travis, { + // json: true, + // body: { + // request: { + // branch: 'master', + // config: { + // merge_mode: 'deep_merge', + // env: { + // NEXE_VERSION: release + // } + // } + // } + // }, + // headers: { + // 'Travis-API-Version': '3', + // 'Authorization': `token ${env.TRAVIS_TOKEN}` + // } + // }) +} + +export function triggerMacBuild (release: NexeTarget) { + return Promise.resolve() + // assert.ok(env.CIRCLE_TOKEN) + // const circle = `https://circleci.com/api/v1.1/project/github/nexe/nexe/tree/master?circle-token=${env.CIRCLE_TOKEN}` + // return got(circle, { + // json: true, + // body: { + // build_parameters: { + // NEXE_VERSION: release + // } + // } + // }) +} + +export function triggerWindowsBuild (release: NexeTarget) { + return Promise.resolve(env.NEXE_VERSION = release.toString()) +} diff --git a/tasks/releases.ts b/tasks/releases.ts new file mode 100644 index 0000000..ab1dc5b --- /dev/null +++ b/tasks/releases.ts @@ -0,0 +1,62 @@ +import got = require('got') +import { + NodePlatform, + NodeArch, + platforms, + architectures, + NexeTarget, + getTarget, + targetsEqual, +} from '../src/target' +export { NexeTarget } + +export interface GitRelease { + tag_name: string + assets_url: string + upload_url: string + assets: Array<{ name: string }> +} + +interface NodeRelease { + version: string +} + +async function getJson (url: string) { + return JSON.parse((await got(url)).body) as T +} + +//TODO only build the latest of each major...? +function isBuildableVersion (version: string) { + const major = +version.split('.')[0] + return ![0, 1, 2, 3, 4, 5, 7].includes(major) + || version === '4.8.4' +} + +export function getLatestGitRelease () { + return getJson('https://api.github.com/repos/nexe/nexe/releases/latest') +} + +export async function getUnBuiltReleases () { + const nodeReleases = (await getJson('https://nodejs.org/download/release/index.json')) + const existingVersions = (await getLatestGitRelease()) + .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 + versions.push(getTarget({ platform, arch, version })) + }) + }) + return versions + }, []) + .filter( + x => !existingVersions.some(t => targetsEqual(t, x)) + ) +} diff --git a/tsconfig.json b/tsconfig.json index b77c0e3..57c70f2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,6 @@ "strict": true }, "include":[ - "src/**/*.ts", - "src/patches/sourcemaps.js" + "src/**/*.ts" ] }