Compare commits

...

27 Commits

Author SHA1 Message Date
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
26 changed files with 1194 additions and 507 deletions
+1 -1
View File
@@ -5,4 +5,4 @@ export NVS_HOME="$HOME/.nvs"
set -e -x set -e -x
yarn run asset-compile npm run asset-compile
+2 -2
View File
@@ -8,13 +8,13 @@ jobs:
- checkout - checkout
- restore_cache: - restore_cache:
keys: keys:
- package-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }} - package-{{ checksum "package.json" }}-{{ checksum "package-lock.json" }}
- package- # Fallback for the latest package.json cache - package- # Fallback for the latest package.json cache
- run: - run:
name: setup name: setup
command: .circleci/setup.sh command: .circleci/setup.sh
- save_cache: - save_cache:
key: package-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }} key: package-{{ checksum "package.json" }}-{{ checksum "package-lock.json" }}
paths: paths:
- node_modules - node_modules
- run: - run:
+3 -4
View File
@@ -1,11 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -e
echo "setting up node to use latest lts"
export NVS_HOME="$HOME/.nvs" export NVS_HOME="$HOME/.nvs"
git clone --depth=1 https://github.com/jasongin/nvs "$NVS_HOME" git clone --depth=1 https://github.com/jasongin/nvs "$NVS_HOME"
. "$NVS_HOME/nvs.sh" install . "$NVS_HOME/nvs.sh" install
nvs add lts && nvs link lts nvs use default nvs add lts && nvs link lts nvs use default
npm install -g yarn node -v
yarn npm -v
npm i
+1 -2
View File
@@ -4,6 +4,5 @@ end_of_line = lf
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
#https://github.com/Microsoft/vscode/issues/23983#event-1087502108 trim_trailing_whitespace = true
#trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
+1
View File
@@ -1,3 +1,4 @@
dist: xenial
language: node_js language: node_js
node_js: node_js:
- '6' - '6'
+1 -1
View File
@@ -1,3 +1,3 @@
{ {
"recommendations": [ "eg2.tslint" ] "recommendations": [ "ms-vscode.vscode-typescript-tslint-plugin" ]
} }
+13 -8
View File
@@ -7,7 +7,6 @@
<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/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/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> <a href="https://www.npmjs.com/package/nexe"><img src="https://img.shields.io/npm/l/nexe.svg" alt="License"></a>
<a href="https://gitter.im/nexe/nexe?utm_source=badge&utm_medium=badge"><img src="https://badges.gitter.im/nexe/nexe.svg"></a>
</p> </p>
<p align="center"><code>npm i nexe -g</code></p> <p align="center"><code>npm i nexe -g</code></p>
@@ -43,7 +42,6 @@ For more CLI options see: `nexe --help`
### Examples ### Examples
- `nexe server.js -r "public/**/*.html"` - `nexe server.js -r "public/**/*.html"`
- `nexe my-bundle.js --no-bundle -o app.exe`
- `nexe --build` - `nexe --build`
- `nexe -t x86-8.0.0` - `nexe -t x86-8.0.0`
@@ -90,14 +88,14 @@ compile({
- Output executable file path - Output executable file path
- default: same as `name` with an OS specific extension. - default: same as `name` with an OS specific extension.
- #### `target: string | object` - #### `target: string | object`
- An object or string describing platform-arch-version. e.g. `'windows-ia32-6.10.3'` - 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 - each segment is optional, and will be merged with the current environment
- Examples: ([full list](https://github.com/nexe/nexe/releases)) - Examples: ([full list](https://github.com/nexe/nexe/releases))
- `'win32-x86-8.6.0` - `'win32-x86-10.13.0`
- `{ platform: 'alpine' }` - `{ platform: 'alpine' }`
- `darwin-8.6.0` - `darwin-10.13.0`
- `linux-x64` - `linux-x64`
- `macos-8.4.0` - `macos-10.13.0`
See [test/target.spec.ts](test/target.spec.ts) 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. - If the [`build`](#build-boolean) flag is set, the platform portion of the target is ignored.
@@ -115,8 +113,15 @@ compile({
- #### `cwd: string` - #### `cwd: string`
- Directory nexe will operate on as though it is the cwd - Directory nexe will operate on as though it is the cwd
- default: process.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: 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) - 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` - #### `python: string`
- On Linux this is the path pointing to your python2 executable - On Linux this is the path pointing to your python2 executable
- On Windows this is the directory where `python` can be accessed - On Windows this is the directory where `python` can be accessed
@@ -145,12 +150,12 @@ compile({
- #### `temp: string` - #### `temp: string`
- Path to use for storing nexe's build files - Path to use for storing nexe's build files
- Override in the env with `NEXE_TEMP` - Override in the env with `NEXE_TEMP`
- default: `./.nexe` in the cwd - default: `~/.nexe`
- #### `ico: string` - #### `ico: string`
- Path to a user provided icon to be used (Windows only). Requires `--build` to be set. - Path to a user provided icon to be used (Windows only). Requires `--build` to be set.
- #### `rc: object` - #### `rc: object`
- Settings for patching the [node.rc](https://github.com/nodejs/node/blob/master/src/res/node.rc) configuration file (Windows only). - Settings for patching the [node.rc](https://github.com/nodejs/node/blob/master/src/res/node.rc) configuration file (Windows only).
- Example: - Example (keys may vary depending ont the version. Reference the file linked above):
```javascript ```javascript
{ {
CompanyName: "ACME Corp", CompanyName: "ACME Corp",
+2
View File
@@ -9,6 +9,8 @@ environment:
GITHUB_TOKEN: GITHUB_TOKEN:
secure: 3wuEGJsppSKFvdwhp3jprUA9Zkc3WINlc/9oFXjhAR6J05lYMXP7Fl7lXTKpkGZx secure: 3wuEGJsppSKFvdwhp3jprUA9Zkc3WINlc/9oFXjhAR6J05lYMXP7Fl7lXTKpkGZx
install: install:
- rmdir C:\cygwin /s /q
- rmdir C:\QT /s /q
- ps: Install-Product node 8 - ps: Install-Product node 8
- curl -L -o nasm.exe https://www.nasm.us/pub/nasm/releasebuilds/2.13.03/win64/nasm-2.13.03-installer-x64.exe - curl -L -o nasm.exe https://www.nasm.us/pub/nasm/releasebuilds/2.13.03/win64/nasm-2.13.03-installer-x64.exe
- start /wait nasm.exe /S - start /wait nasm.exe /S
+14 -1
View File
@@ -10,7 +10,20 @@ if (require.main === module) {
process.stderr.write(showHelp ? options.help : options.version + eol) process.stderr.write(showHelp ? options.help : options.version + eol)
} else { } else {
const nexe = require('./lib/nexe') const nexe = require('./lib/nexe')
nexe.compile(argv).catch(() => { 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) process.exit(1)
}) })
} }
+844 -239
View File
File diff suppressed because it is too large Load Diff
+19 -18
View File
@@ -2,7 +2,7 @@
"name": "nexe", "name": "nexe",
"description": "Create a single executable out of your Node.js application", "description": "Create a single executable out of your Node.js application",
"license": "MIT", "license": "MIT",
"version": "3.0.0-beta.9", "version": "3.0.2",
"contributors": [ "contributors": [
"Craig Condon <craig.j.condon@gmail.com> (http://crcn.io)", "Craig Condon <craig.j.condon@gmail.com> (http://crcn.io)",
"Jared Allard <jaredallard@outlook.com>", "Jared Allard <jaredallard@outlook.com>",
@@ -36,40 +36,41 @@
"@calebboyd/semaphore": "^1.3.1", "@calebboyd/semaphore": "^1.3.1",
"app-builder": "^5.2.0", "app-builder": "^5.2.0",
"caw": "^2.0.1", "caw": "^2.0.1",
"chalk": "^2.4.1", "chalk": "^2.4.2",
"cherow": "^1.6.8", "cherow": "1.6.9",
"download": "^7.1.0", "download": "^7.1.0",
"globby": "^8.0.1", "globby": "^9.1.0",
"got": "^9.3.2", "got": "^9.6.0",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"multistream": "^2.1.1", "multistream": "^2.1.1",
"ora": "^3.0.0", "ora": "^3.2.0",
"pify": "^4.0.1", "pify": "^4.0.1",
"resolve-dependencies": "^2.2.0", "resolve-dependencies": "^2.2.2",
"rimraf": "^2.6.2" "rimraf": "^2.6.3"
}, },
"devDependencies": { "devDependencies": {
"@types/chai": "^4.1.7", "@types/chai": "^4.1.7",
"@types/download": "^6.2.4",
"@types/execa": "^0.9.0", "@types/execa": "^0.9.0",
"@types/globby": "^8.0.0", "@types/globby": "^9.1.0",
"@types/got": "^9.2.0", "@types/got": "^9.4.1",
"@types/minimist": "^1.2.0", "@types/minimist": "^1.2.0",
"@types/mkdirp": "^0.5.2", "@types/mkdirp": "^0.5.2",
"@types/mocha": "^5.2.5", "@types/mocha": "^5.2.6",
"@types/multistream": "^2.1.1", "@types/multistream": "^2.1.1",
"@types/ora": "^1.3.4", "@types/ora": "^3.2.0",
"@types/pify": "3.0.2", "@types/pify": "3.0.2",
"@types/rimraf": "2.0.2", "@types/rimraf": "2.0.2",
"@types/semver": "^5.5.0", "@types/semver": "^5.5.0",
"chai": "^4.2.0", "chai": "^4.2.0",
"execa": "^1.0.0", "execa": "^1.0.0",
"mocha": "^5.2.0", "mocha": "^6.0.2",
"prettier": "^1.15.2", "prettier": "^1.16.4",
"ts-node": "^7.0.1", "ts-node": "^8.0.3",
"tslint": "^5.11.0", "tslint": "^5.14.0",
"tslint-config-prettier": "^1.16.0", "tslint-config-prettier": "^1.18.0",
"tslint-plugin-prettier": "^2.0.1", "tslint-plugin-prettier": "^2.0.1",
"typescript": "^3.1.6" "typescript": "^3.4.1"
} }
} }
+44 -60
View File
@@ -1,7 +1,6 @@
import { delimiter, dirname, normalize, join } from 'path' import { delimiter, resolve, normalize, join } from 'path'
import { Buffer } from 'buffer' import { Buffer } from 'buffer'
import { createReadStream } from 'fs' import { createReadStream, ReadStream } from 'fs'
import { Readable, Stream } from 'stream'
import { spawn } from 'child_process' import { spawn } from 'child_process'
import { Logger, LogStep } from './logger' import { Logger, LogStep } from './logger'
import { import {
@@ -16,9 +15,6 @@ import {
} from './util' } from './util'
import { NexeOptions, version } from './options' import { NexeOptions, version } from './options'
import { NexeTarget } from './target' import { NexeTarget } from './target'
import download = require('download')
import { getLatestGitRelease } from './releases'
import { IncomingMessage } from 'http'
import combineStreams = require('multistream') import combineStreams = require('multistream')
import { Bundle, toStream } from './fs/bundle' import { Bundle, toStream } from './fs/bundle'
@@ -26,6 +22,8 @@ const isBsd = Boolean(~process.platform.indexOf('bsd'))
const make = isWindows ? 'vcbuild.bat' : isBsd ? 'gmake' : 'make' const make = isWindows ? 'vcbuild.bat' : isBsd ? 'gmake' : 'make'
const configure = isWindows ? 'configure' : './configure' const configure = isWindows ? 'configure' : './configure'
type StringReplacer = (match: string) => string
export interface NexeFile { export interface NexeFile {
filename: string filename: string
absPath: string absPath: string
@@ -34,22 +32,28 @@ export interface NexeFile {
export { NexeOptions } export { NexeOptions }
export class NexeError extends Error {
constructor(m: string) {
super(m)
Object.setPrototypeOf(this, NexeError.prototype)
}
}
export class NexeCompiler { export class NexeCompiler {
/** /**
* Epoch of when compilation started * Epoch of when compilation started
*/ */
private start = Date.now() private start = Date.now()
private compileStep: LogStep | undefined
public log = new Logger(this.options.loglevel)
/** /**
* Copy of process.env * Copy of process.env
*/ */
private env = { ...process.env } public env = { ...process.env }
/** /**
* Virtual FileSystem * Virtual FileSystem
*/ */
private bundle: Bundle public bundle: Bundle
private compileStep: LogStep | undefined
public log = new Logger(this.options.loglevel)
/** /**
* Root directory for the source of the current build * Root directory for the source of the current build
*/ */
@@ -82,6 +86,10 @@ export class NexeCompiler {
* Output filename (-o myapp.exe) * Output filename (-o myapp.exe)
*/ */
public output = this.options.output public output = this.options.output
/**
* Flag to indicate whether or notstdin was used for input
*/
public stdinUsed = false
/** /**
* Path to the configure script * Path to the configure script
*/ */
@@ -89,12 +97,23 @@ export class NexeCompiler {
/** /**
* The file path of node binary * The file path of node binary
*/ */
private nodeSrcBinPath: string public nodeSrcBinPath: string
/**
* Remote asset path if available
*/
public remoteAsset: string
constructor(public options: NexeOptions) { constructor(public options: NexeOptions) {
const { python } = (this.options = options) const { python } = (this.options = options)
//SOMEDAY iterate over multiple targets with `--outDir`
this.targets = options.targets as NexeTarget[] this.targets = options.targets as NexeTarget[]
this.target = this.targets[0] 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.src = join(this.options.temp, this.target.version)
this.configureScript = configure + (semverGt(this.target.version, '10.10.0') ? '.py' : '') this.configureScript = configure + (semverGt(this.target.version, '10.10.0') ? '.py' : '')
this.nodeSrcBinPath = isWindows this.nodeSrcBinPath = isWindows
@@ -155,9 +174,9 @@ export class NexeCompiler {
} }
@bound @bound
async replaceInFileAsync(file: string, replace: string | RegExp, value: string) { async replaceInFileAsync(file: string, replace: string | RegExp, value: string | StringReplacer) {
const entry = await this.readFileAsync(file) const entry = await this.readFileAsync(file)
entry.contents = entry.contents.replace(replace, value) entry.contents = entry.contents.replace(replace, value as any)
} }
@bound @bound
@@ -166,19 +185,22 @@ export class NexeCompiler {
entry.contents = contents entry.contents = contents
} }
quit() { quit(error?: any) {
const time = Date.now() - this.start const time = Date.now() - this.start
this.log.write(`Finished in ${time / 1000}s`) this.log.write(`Finished in ${time / 1000}s`, error ? 'red' : 'green')
return this.log.flush() return this.log.flush()
} }
assertBuild() { assertBuild() {
if (!this.options.build) { if (!this.options.build) {
throw new Error('This feature is only available with `--build`') throw new NexeError('This feature is only available with `--build`')
} }
} }
public getNodeExecutableLocation(target?: NexeTarget) { public getNodeExecutableLocation(target?: NexeTarget) {
if (this.options.asset && !this.options.asset.startsWith('http')) {
return resolve(this.options.cwd, this.options.asset)
}
if (target) { if (target) {
return join(this.options.temp, target.toString()) return join(this.options.temp, target.toString())
} }
@@ -207,7 +229,7 @@ export class NexeCompiler {
} }
if (code != 0) { if (code != 0) {
const error = `${command} ${args.join(' ')} exited with code: ${code}` const error = `${command} ${args.join(' ')} exited with code: ${code}`
reject(new Error(error)) reject(new NexeError(error))
} }
resolve() resolve()
}) })
@@ -221,7 +243,7 @@ export class NexeCompiler {
]) ])
} }
private async _buildAsync() { public async build(): Promise<ReadStream> {
this.compileStep!.log( this.compileStep!.log(
`Configuring node build${ `Configuring node build${
this.options.configure.length ? ': ' + this.options.configure : '...' this.options.configure.length ? ': ' + this.options.configure : '...'
@@ -236,44 +258,11 @@ export class NexeCompiler {
return createReadStream(this.getNodeExecutableLocation()) return createReadStream(this.getNodeExecutableLocation())
} }
private async _fetchPrebuiltBinaryAsync(target: NexeTarget) {
let downloadOptions = this.options.downloadOptions
if (this.options.ghToken) {
downloadOptions = Object.assign({}, downloadOptions)
downloadOptions.headers = Object.assign({}, downloadOptions.headers, {
Authorization: 'token ' + this.options.ghToken
})
}
const githubRelease = await getLatestGitRelease(downloadOptions)
const assetName = target.toString()
const asset = githubRelease.assets.find(x => x.name === assetName)
if (!asset) {
throw new Error(`${assetName} not available, create it using the --build flag`)
}
const filename = this.getNodeExecutableLocation(target)
await download(asset.browser_download_url, dirname(filename), this.options.downloadOptions).on(
'response',
(res: IncomingMessage) => {
const total = +res.headers['content-length']!
let current = 0
res.on('data', data => {
current += data.length
this.compileStep!.modify(`Downloading...${((current / total) * 100).toFixed()}%`)
})
}
)
return createReadStream(filename)
}
private async _shouldCompileBinaryAsync( private async _shouldCompileBinaryAsync(
binary: NodeJS.ReadableStream | null, binary: NodeJS.ReadableStream | null,
location: string | undefined location: string | undefined
) { ) {
//TODO combine make/configure/vcBuild/and modified times of included files //SOMEDAY combine make/configure/vcBuild/and modified times of included files
const { snapshot, build } = this.options const { snapshot, build } = this.options
if (!binary) { if (!binary) {
@@ -283,8 +272,6 @@ export class NexeCompiler {
if (build && snapshot != null && (await pathExistsAsync(snapshot))) { if (build && snapshot != null && (await pathExistsAsync(snapshot))) {
const snapshotLastModified = (await statAsync(snapshot)).mtimeMs const snapshotLastModified = (await statAsync(snapshot)).mtimeMs
const binaryLastModified = (await statAsync(location)).mtimeMs const binaryLastModified = (await statAsync(location)).mtimeMs
// if build was requested and there's a snapshot to embed in the binary,
// we need to rebuild if the snapshot was just modified.
return snapshotLastModified > binaryLastModified return snapshotLastModified > binaryLastModified
} }
@@ -296,12 +283,9 @@ export class NexeCompiler {
const build = this.options.build const build = this.options.build
const location = this.getNodeExecutableLocation(build ? undefined : target) const location = this.getNodeExecutableLocation(build ? undefined : target)
let binary = (await pathExistsAsync(location)) ? createReadStream(location) : null let binary = (await pathExistsAsync(location)) ? createReadStream(location) : null
if (!build && !binary) {
step.modify('Fetching prebuilt binary')
binary = await this._fetchPrebuiltBinaryAsync(target)
}
if (await this._shouldCompileBinaryAsync(binary, location)) { if (await this._shouldCompileBinaryAsync(binary, location)) {
binary = await this._buildAsync() binary = await this.build()
step.log('Node binary compiled') step.log('Node binary compiled')
} }
return this._assembleDeliverable(binary!) return this._assembleDeliverable(binary!)
+43 -45
View File
@@ -34,6 +34,8 @@ function shimFs(binary: NexeBinary, fs: any = require('fs')) {
const { blobPath, resources: manifest } = binary const { blobPath, resources: manifest } = binary
const { resourceStart, stat } = binary.layout const { resourceStart, stat } = binary.layout
const directories: { [key: string]: { [key: string]: boolean } } = {}, const directories: { [key: string]: { [key: string]: boolean } } = {},
notAFile = '!@#$%^&*',
isWin = process.platform.startsWith('win'),
isString = (x: any): x is string => typeof x === 'string' || x instanceof String, isString = (x: any): x is string => typeof x === 'string' || x instanceof String,
isNotFile = () => false, isNotFile = () => false,
isNotDirectory = isNotFile, isNotDirectory = isNotFile,
@@ -42,23 +44,25 @@ function shimFs(binary: NexeBinary, fs: any = require('fs')) {
isDirectory = isFile, isDirectory = isFile,
path = require('path') path = require('path')
let log = (text: string) => { let log = (text: string) => true
if ((process.env.DEBUG || '').toLowerCase().includes('nexe:require')) { if ((process.env.DEBUG || '').toLowerCase().includes('nexe:require')) {
process.stderr.write('[nexe] - ' + text + '\n') log = (text: string) => process.stderr.write('[nexe] - ' + text + '\n')
} else {
log = noop
}
} }
const getKey = process.platform.startsWith('win') const getKey = function getKey(filepath: string | Buffer | null): string {
? function getKey(filepath: string): string { if (Buffer.isBuffer(filepath)) {
let key = path.resolve(filepath) filepath = filepath.toString()
if (key.substr(1, 2) === ':\\') { }
key = key[0].toUpperCase() + key.substr(1) if (!isString(filepath)) {
} return notAFile
return key }
} let key = path.resolve(filepath)
: path.resolve
if (isWin && key.substr(1, 2) === ':\\') {
key = key[0].toUpperCase() + key.substr(1)
}
return key
}
const statTime = function() { const statTime = function() {
return { return {
@@ -81,22 +85,21 @@ function shimFs(binary: NexeBinary, fs: any = require('fs')) {
} }
} }
const createStat = function(directoryExtensions: any, fileExtensions?: any) { const createStat = function(extensions: any) {
if (!fileExtensions) { return Object.assign(new fs.Stats(), binary.layout.stat, statTime(), extensions)
return Object.assign({}, binary.layout.stat, directoryExtensions, { size: 0 }, statTime())
}
const size = directoryExtensions[1]
return Object.assign({}, binary.layout.stat, fileExtensions, { size }, statTime())
} }
const ownStat = function(filepath: string) { const ownStat = function(filepath: any) {
setupManifest() setupManifest()
const key = getKey(filepath) const key = getKey(filepath)
if (directories[key]) { if (directories[key]) {
return createStat({ isDirectory, isFile: isNotFile }) let mode = binary.layout.stat.mode
mode |= fs.constants.S_IFDIR
mode &= ~fs.constants.S_IFREG
return createStat({ mode, size: 0 })
} }
if (manifest[key]) { if (manifest[key]) {
return createStat(manifest[key], { isFile, isDirectory: isNotDirectory }) return createStat({ size: manifest[key][1] })
} }
} }
@@ -139,6 +142,8 @@ function shimFs(binary: NexeBinary, fs: any = require('fs')) {
currentDir = path.dirname(currentDir) currentDir = path.dirname(currentDir)
} }
}) })
;(manifest[notAFile] as any) = false
;(directories[notAFile] as any) = false
setupManifest = noop setupManifest = noop
} }
@@ -163,32 +168,26 @@ function shimFs(binary: NexeBinary, fs: any = require('fs')) {
realpathSync: function realpathSync(filepath: any, options: any) { realpathSync: function realpathSync(filepath: any, options: any) {
setupManifest() setupManifest()
const key = getKey(filepath) const key = getKey(filepath)
if (isString(filepath) && (manifest[filepath] || manifest[key])) { if (manifest[key]) {
return filepath return filepath
} }
return originalFsMethods.realpathSync.call(fs, filepath, options) return originalFsMethods.realpathSync.call(fs, filepath, options)
}, },
readdir: function readdir(filepath: string | Buffer, options: any, callback: any) { readdir: function readdir(filepath: string | Buffer, options: any, callback: any) {
setupManifest() setupManifest()
filepath = filepath.toString()
if ('function' === typeof options) {
callback = options
options = { encoding: 'utf8' }
}
const dir = directories[getKey(filepath)] const dir = directories[getKey(filepath)]
if (dir) { if (dir) {
process.nextTick(() => { if ('function' === typeof options) {
//todo merge with original? callback = options
callback(null, Object.keys(dir)) options = { encoding: 'utf8' }
}) }
process.nextTick(() => callback(null, Object.keys(dir)))
} else { } else {
return originalFsMethods.readdir.apply(fs, arguments) return originalFsMethods.readdir.apply(fs, arguments)
} }
}, },
readdirSync: function readdirSync(filepath: string | Buffer, options: any) { readdirSync: function readdirSync(filepath: string | Buffer, options: any) {
setupManifest() setupManifest()
filepath = filepath.toString()
const dir = directories[getKey(filepath)] const dir = directories[getKey(filepath)]
if (dir) { if (dir) {
return Object.keys(dir) return Object.keys(dir)
@@ -198,8 +197,8 @@ function shimFs(binary: NexeBinary, fs: any = require('fs')) {
readFile: function readFile(filepath: any, options: any, callback: any) { readFile: function readFile(filepath: any, options: any, callback: any) {
setupManifest() setupManifest()
const entry = manifest[filepath] || manifest[getKey(filepath)] const entry = manifest[getKey(filepath)]
if (!entry || !isString(filepath)) { if (!entry) {
return originalFsMethods.readFile.apply(fs, arguments) return originalFsMethods.readFile.apply(fs, arguments)
} }
const [offset, length] = entry const [offset, length] = entry
@@ -230,8 +229,8 @@ function shimFs(binary: NexeBinary, fs: any = require('fs')) {
}, },
createReadStream: function createReadStream(filepath: any, options: any) { createReadStream: function createReadStream(filepath: any, options: any) {
setupManifest() setupManifest()
const entry = manifest[filepath] || manifest[getKey(filepath)] const entry = manifest[getKey(filepath)]
if (!entry || !isString(filepath)) { if (!entry) {
return originalFsMethods.createReadStream.apply(fs, arguments) return originalFsMethods.createReadStream.apply(fs, arguments)
} }
const [offset, length] = entry const [offset, length] = entry
@@ -248,9 +247,8 @@ function shimFs(binary: NexeBinary, fs: any = require('fs')) {
}, },
readFileSync: function readFileSync(filepath: any, options: any) { readFileSync: function readFileSync(filepath: any, options: any) {
setupManifest() setupManifest()
const entry = manifest[getKey(filepath)]
const entry = manifest[filepath] || manifest[getKey(filepath)] if (!entry) {
if (!entry || !isString(filepath)) {
return originalFsMethods.readFileSync.apply(fs, arguments) return originalFsMethods.readFileSync.apply(fs, arguments)
} }
const [offset, length] = entry const [offset, length] = entry
@@ -263,14 +261,14 @@ function shimFs(binary: NexeBinary, fs: any = require('fs')) {
return encoding ? result.toString(encoding) : result return encoding ? result.toString(encoding) : result
}, },
statSync: function statSync(filepath: string | Buffer) { statSync: function statSync(filepath: string | Buffer) {
const stat = isString(filepath) && ownStat(filepath) const stat = ownStat(filepath)
if (stat) { if (stat) {
return stat return stat
} }
return originalFsMethods.statSync.apply(fs, arguments) return originalFsMethods.statSync.apply(fs, arguments)
}, },
stat: function stat(filepath: string | Buffer, callback: any) { stat: function stat(filepath: string | Buffer, callback: any) {
const stat = isString(filepath) && ownStat(filepath) const stat = ownStat(filepath)
if (stat) { if (stat) {
process.nextTick(() => { process.nextTick(() => {
callback(null, stat) callback(null, stat)
+2 -2
View File
@@ -1,5 +1,5 @@
import colors = require('chalk') import colors from 'chalk'
import ora = require('ora') import ora from 'ora'
const frameLength = 120 const frameLength = 120
+17 -19
View File
@@ -1,6 +1,6 @@
import { EOL } from 'os' import { EOL } from 'os'
import { compose } from 'app-builder' import { compose } from 'app-builder'
import { NexeCompiler } from './compiler' import { NexeCompiler, NexeError } from './compiler'
import { normalizeOptions, NexeOptions, NexePatch } from './options' import { normalizeOptions, NexeOptions, NexePatch } from './options'
import resource from './steps/resource' import resource from './steps/resource'
import clean from './steps/clean' import clean from './steps/clean'
@@ -15,31 +15,29 @@ async function compile(
compilerOptions?: Partial<NexeOptions>, compilerOptions?: Partial<NexeOptions>,
callback?: (err: Error | null) => void callback?: (err: Error | null) => void
) { ) {
const options = normalizeOptions(compilerOptions) let error = null,
const compiler = new NexeCompiler(options) options: NexeOptions | null = null,
compiler: NexeCompiler | null = null
const nexe = compose(
clean,
resource,
cli,
bundle,
shim,
options.build ? [download, artifacts, ...patches, ...(options.patches as NexePatch[])] : [],
options.plugins as NexePatch[]
)
let error = null
try { try {
await nexe(compiler) 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) { } catch (e) {
error = e error = e
} }
if (error) { if (error) {
if (compiler.options.loglevel !== 'silent' && error) { compiler && compiler.quit(error)
process.stderr.write(EOL + error.message + EOL)
}
compiler.quit()
if (callback) return callback(error) if (callback) return callback(error)
return Promise.reject(error) return Promise.reject(error)
} }
+39 -33
View File
@@ -1,6 +1,6 @@
import * as parseArgv from 'minimist' import * as parseArgv from 'minimist'
import { NexeCompiler } from './compiler' import { NexeCompiler, NexeError } from './compiler'
import { isWindows } from './util' import { isWindows, STDIN_FLAG } from './util'
import { basename, extname, join, isAbsolute, resolve } from 'path' import { basename, extname, join, isAbsolute, resolve } from 'path'
import { getTarget, NexeTarget } from './target' import { getTarget, NexeTarget } from './target'
import { EOL, homedir } from 'os' import { EOL, homedir } from 'os'
@@ -21,6 +21,7 @@ export interface NexeOptions {
output: string output: string
targets: (string | NexeTarget)[] targets: (string | NexeTarget)[]
name: string name: string
asset: string
cwd: string cwd: string
flags: string[] flags: string[]
configure: string[] configure: string[]
@@ -35,11 +36,6 @@ export interface NexeOptions {
patches: (string | NexePatch)[] patches: (string | NexePatch)[]
plugins: (string | NexePatch)[] plugins: (string | NexePatch)[]
native: any native: any
/**
* @deprecated
* Use mangle: false instead
*/
empty: boolean
mangle: boolean mangle: boolean
ghToken: string ghToken: string
sourceUrl?: string sourceUrl?: string
@@ -53,7 +49,6 @@ export interface NexeOptions {
ico?: string ico?: string
debugBundle?: boolean debugBundle?: boolean
warmup?: string warmup?: string
compress?: boolean
clean?: boolean clean?: boolean
/** /**
* Api Only * Api Only
@@ -65,11 +60,11 @@ const defaults = {
flags: [], flags: [],
cwd: process.cwd(), cwd: process.cwd(),
configure: [], configure: [],
mangle: true,
make: [], make: [],
targets: [], targets: [],
vcBuild: isWindows ? ['nosign', 'release'] : [], vcBuild: isWindows ? ['nosign', 'release'] : [],
enableNodeCli: false, enableNodeCli: false,
compress: false,
build: false, build: false,
bundle: true, bundle: true,
patches: [], patches: [],
@@ -104,6 +99,7 @@ ${c.bold('nexe <entry-file> [options]')}
-t --target -- node version description -t --target -- node version description
-n --name -- main app module name -n --name -- main app module name
-r --resource -- *embed files (glob) within the binary -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 --plugin -- extend nexe runtime behavior
${c.underline.bold('Building from source:')} ${c.underline.bold('Building from source:')}
@@ -113,7 +109,8 @@ ${c.bold('nexe <entry-file> [options]')}
-f --flag -- *v8 flags to include during compilation -f --flag -- *v8 flags to include during compilation
-c --configure -- *arguments to the configure step -c --configure -- *arguments to the configure step
-m --make -- *arguments to the make/build step -m --make -- *arguments to the make/build step
--no-mangle -- used when generating base binaries, or when pathing _third_party_main manually. --patch -- module with middelware 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 --snapshot -- path to a warmup snapshot
--ico -- file name for alternate icon file (windows) --ico -- file name for alternate icon file (windows)
--rc-* -- populate rc file options (windows) --rc-* -- populate rc file options (windows)
@@ -165,7 +162,7 @@ function extractLogLevel(options: NexeOptions) {
} }
function isName(name: string) { function isName(name: string) {
return name && name !== 'index' return name && name !== 'index' && name !== STDIN_FLAG
} }
function extractName(options: NexeOptions) { function extractName(options: NexeOptions) {
@@ -194,11 +191,16 @@ function isEntryFile(filename?: string): filename is string {
return Boolean(filename && !isAbsolute(filename)) return Boolean(filename && !isAbsolute(filename))
} }
export function resolveEntry(input: string, cwd: string, maybeEntry?: string) { export function resolveEntry(
if (input === '-' || maybeEntry === '-') { input: string,
return '' cwd: string,
} maybeEntry: string | undefined,
bundle: boolean | string
) {
let result = null let result = null
if (input === '-' || maybeEntry === '-') {
return STDIN_FLAG
}
if (input && isAbsolute(input)) { if (input && isAbsolute(input)) {
return input return input
} }
@@ -206,19 +208,19 @@ export function resolveEntry(input: string, cwd: string, maybeEntry?: string) {
const inputPath = padRelative(input) const inputPath = padRelative(input)
result = resolveFileNameSync(cwd, inputPath) result = resolveFileNameSync(cwd, inputPath)
} }
if (isEntryFile(maybeEntry) && (!result || !result.absPath)) { if (isEntryFile(maybeEntry) && (!result || !result.absPath)) {
const inputPath = padRelative(maybeEntry) const inputPath = padRelative(maybeEntry)
result = resolveFileNameSync(cwd, inputPath) result = resolveFileNameSync(cwd, inputPath)
} }
if (!process.stdin.isTTY) { if (!process.stdin.isTTY && (!result || !result.absPath) && bundle === defaults.bundle) {
return '' return STDIN_FLAG
} }
if (!result || !result.absPath) { if (!result || !result.absPath) {
result = resolveFileNameSync(cwd, '.') result = resolveFileNameSync(cwd, '.')
} }
if (!result.absPath) throw new Error(`Entry file "${input}" not found!`) if (!result.absPath) {
throw new NexeError(`Entry file "${input || ''}" not found!`)
}
return result.absPath return result.absPath
} }
@@ -234,8 +236,8 @@ function normalizeOptions(input?: Partial<NexeOptions>): NexeOptions {
? resolve(cwd, options.temp) ? resolve(cwd, options.temp)
: process.env.NEXE_TEMP || join(homedir(), '.nexe') : process.env.NEXE_TEMP || join(homedir(), '.nexe')
const maybeEntry = isCli(input) ? argv._[argv._.length - 1] : undefined const maybeEntry = isCli(input) ? argv._[argv._.length - 1] : undefined
options.input = resolveEntry(options.input, cwd, maybeEntry) options.input = resolveEntry(options.input, cwd, maybeEntry, options.bundle)
options.enableStdIn = isCli(input) && options.input === '' options.enableStdIn = isCli(input) && options.input === STDIN_FLAG
options.name = extractName(options) options.name = extractName(options)
options.loglevel = extractLogLevel(options) options.loglevel = extractLogLevel(options)
options.flags = flatten(opts.flag, options.flags) options.flags = flatten(opts.flag, options.flags)
@@ -263,6 +265,21 @@ function normalizeOptions(input?: Partial<NexeOptions>): NexeOptions {
: `${options.output || options.name}` : `${options.output || options.name}`
options.output = resolve(cwd, options.output) 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) { if (options.build) {
const { arch } = options.targets[0] as NexeTarget const { arch } = options.targets[0] as NexeTarget
if (isWindows) { if (isWindows) {
@@ -272,17 +289,6 @@ function normalizeOptions(input?: Partial<NexeOptions>): NexeOptions {
} }
} }
const requireDefault = (x: string) => {
if (typeof x === 'string') {
return require(x).default
}
return x
}
options.mangle = 'mangle' in opts ? opts.mangle : !opts.empty
options.plugins = flatten(opts.plugin, options.plugins).map(requireDefault)
options.patches = flatten(opts.patch, options.patches).map(requireDefault)
Object.keys(alias) Object.keys(alias)
.filter(k => k !== 'rc') .filter(k => k !== 'rc')
.forEach(x => delete opts[x]) .forEach(x => delete opts[x])
+8 -5
View File
@@ -1,17 +1,20 @@
const fs = require('fs'), const fs = require('fs'),
fd = fs.openSync(process.execPath, 'r'), fd = fs.openSync(process.execPath, 'r'),
stat = fs.statSync(process.execPath), stat = fs.statSync(process.execPath),
footer = Buffer.alloc(32, 0) tailSize = Math.min(stat.size, 16000),
tailWindow = Buffer.from(Array(tailSize))
fs.readSync(fd, footer, 0, 32, stat.size - 32) fs.readSync(fd, tailWindow, 0, tailSize, stat.size - tailSize)
if (!footer.slice(0, 16).equals(Buffer.from('<nexe~~sentinel>'))) { const footerPosition = tailWindow.indexOf('<nexe~~sentinel>')
if (footerPosition == -1) {
throw 'Invalid Nexe binary' throw 'Invalid Nexe binary'
} }
const contentSize = footer.readDoubleLE(16), const footer = tailWindow.slice(footerPosition, footerPosition + 32),
contentSize = footer.readDoubleLE(16),
resourceSize = footer.readDoubleLE(24), resourceSize = footer.readDoubleLE(24),
contentStart = stat.size - 32 - resourceSize - contentSize, contentStart = stat.size - tailSize + footerPosition - resourceSize - contentSize,
resourceStart = contentStart + contentSize resourceStart = contentStart + contentSize
Object.defineProperty( Object.defineProperty(
+20 -1
View File
@@ -6,8 +6,27 @@ export default async function disableNodeCli(compiler: NexeCompiler, next: () =>
return next() return next()
} }
if (semverGt(compiler.target.version, '9.99')) { 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(') 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 { } else {
const nodeccMarker = 'argv[index][0] ==' const nodeccMarker = 'argv[index][0] =='
+44 -4
View File
@@ -1,20 +1,60 @@
import { NexeCompiler } from '../compiler' import { NexeCompiler, NexeError } from '../compiler'
import { resolve } from 'path' import { resolve, relative } from 'path'
import { each } from '@calebboyd/semaphore' import { each } from '@calebboyd/semaphore'
import resolveFiles from 'resolve-dependencies' import resolveFiles from 'resolve-dependencies'
import { dequote, STDIN_FLAG } from '../util'
import { Readable } from 'stream'
function getStdIn(stdin: Readable): Promise<string> {
return new Promise(resolve => {
let out = ''
stdin
.setEncoding('utf8')
.on('readable', () => {
let current
while ((current = stdin.read())) {
out += current
}
})
.on('end', () => resolve(out))
})
}
export default async function bundle(compiler: NexeCompiler, next: any) { export default async function bundle(compiler: NexeCompiler, next: any) {
const { bundle, cwd, input } = compiler.options const { bundle, cwd, input } = compiler.options
compiler.entrypoint = './' + relative(cwd, input)
compiler.startup = ';require("module").runMain();'
if (!bundle) { if (!bundle) {
await compiler.addResource(resolve(cwd, input)) await compiler.addResource(resolve(cwd, input))
return next() return next()
} }
if (!input) { let code = ''
if (typeof bundle === 'string') {
code = await require(bundle).createBundle(compiler.options)
}
if (input === STDIN_FLAG) {
compiler.stdinUsed = true
compiler.entrypoint = './__nexe_stdin.js'
code = code || dequote(await getStdIn(process.stdin))
await compiler.addResource(resolve(cwd, compiler.entrypoint), code)
return next() return next()
} }
const { files } = await resolveFiles(input, { cwd, expand: true, loadContent: false }) 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), { await each(Object.keys(files), (filename: string) => compiler.addResource(filename), {
concurrency: 10 concurrency: 10
}) })
+20 -42
View File
@@ -1,24 +1,9 @@
import { normalize, relative, resolve } from 'path' import { dirname, normalize, relative } from 'path'
import { createWriteStream, chmodSync, statSync } from 'fs' import { createWriteStream, chmodSync, statSync } from 'fs'
import { dequote } from '../util'
import { Readable } from 'stream'
import { NexeCompiler } from '../compiler' import { NexeCompiler } from '../compiler'
import { NexeTarget } from '../target' import { NexeTarget } from '../target'
import { STDIN_FLAG } from '../util'
function getStdIn(stdin: Readable): Promise<string> { import mkdirp = require('mkdirp')
return new Promise(resolve => {
let out = ''
stdin
.setEncoding('utf8')
.on('readable', () => {
let current
while ((current = stdin.read())) {
out += current
}
})
.on('end', () => resolve(out))
})
}
/** /**
* The "cli" step detects the appropriate input. If no input options are passed, * The "cli" step detects the appropriate input. If no input options are passed,
@@ -26,49 +11,42 @@ function getStdIn(stdin: Readable): Promise<string> {
* After all the build steps have run, the output (the executable) is written to a file or piped to stdout. * After all the build steps have run, the output (the executable) is written to a file or piped to stdout.
* *
* Configuration: * Configuration:
* - compiler.options.input - file path to the input bundle.
* - fallbacks: stdin, package.json#main
* - compiler.options.output - file path to the output executable.
* - fallbacks: stdout, nexe_ + epoch + ext
* *
* @param {*} compiler * @param {*} compiler
* @param {*} next * @param {*} next
*/ */
export default async function cli(compiler: NexeCompiler, next: () => Promise<void>) { export default async function cli(compiler: NexeCompiler, next: () => Promise<void>) {
const { log } = compiler
let stdInUsed = false
if (!process.stdin.isTTY && compiler.options.enableStdIn) {
stdInUsed = true
compiler.entrypoint = './__nexe_stdin.js'
const code = dequote(await getStdIn(process.stdin))
await compiler.addResource(resolve(compiler.options.cwd, compiler.entrypoint), code)
} else {
compiler.entrypoint = './' + relative(compiler.options.cwd, compiler.options.input)
}
compiler.startup = ';require("module").runMain();'
await next() await next()
const { log } = compiler,
target = compiler.options.targets.shift() as NexeTarget,
deliverable = await compiler.compileAsync(target),
output = normalize(compiler.output!)
const target = compiler.options.targets.shift() as NexeTarget mkdirp.sync(dirname(output))
const deliverable = await compiler.compileAsync(target)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const step = log.step('Writing result to file') const step = log.step('Writing result to file')
deliverable deliverable
.pipe(createWriteStream(normalize(compiler.output!))) .pipe(createWriteStream(output))
.on('error', reject) .on('error', reject)
.once('close', (e: Error) => { .once('close', (e: Error) => {
if (e) { if (e) {
reject(e) reject(e)
} else if (compiler.output) { } else if (compiler.output) {
const output = compiler.output const output = compiler.output,
const mode = statSync(output).mode | 0o111 mode = statSync(output).mode | 0o111,
inputFileLogOutput = relative(process.cwd(), compiler.options.input),
outputFileLogOutput = relative(process.cwd(), output)
chmodSync(output, mode.toString(8).slice(-3)) chmodSync(output, mode.toString(8).slice(-3))
const inputFile = relative(process.cwd(), compiler.options.input)
const outputFile = relative(process.cwd(), output)
step.log( step.log(
`Entry: '${ `Entry: '${
stdInUsed ? (compiler.options.mangle ? '[stdin]' : '[none]') : inputFile compiler.stdinUsed
}' written to: ${outputFile}` ? compiler.options.mangle
? STDIN_FLAG
: '[none]'
: inputFileLogOutput
}' written to: ${outputFileLogOutput}`
) )
resolve(compiler.quit()) resolve(compiler.quit())
} }
+42 -12
View File
@@ -2,7 +2,8 @@ import download = require('download')
import { pathExistsAsync } from '../util' import { pathExistsAsync } from '../util'
import { LogStep } from '../logger' import { LogStep } from '../logger'
import { IncomingMessage } from 'http' import { IncomingMessage } from 'http'
import { NexeCompiler } from '../compiler' import { NexeCompiler, NexeError } from '../compiler'
import { dirname } from 'path'
function fetchNodeSourceAsync(dest: string, url: string, step: LogStep, options = {}) { function fetchNodeSourceAsync(dest: string, url: string, step: LogStep, options = {}) {
const setText = (p: number) => step.modify(`Downloading Node: ${p.toFixed()}%...`) const setText = (p: number) => step.modify(`Downloading Node: ${p.toFixed()}%...`)
@@ -21,27 +22,56 @@ function fetchNodeSourceAsync(dest: string, url: string, step: LogStep, options
.then(() => step.log(`Node source extracted to: ${dest}`)) .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 * Downloads the node source to the configured temporary directory
* @param {*} compiler * @param {*} compiler
* @param {*} next * @param {*} next
*/ */
export default async function downloadNode(compiler: NexeCompiler, next: () => Promise<void>) { export default async function downloadNode(compiler: NexeCompiler, next: () => Promise<void>) {
const { const { src, log, target } = compiler,
src, { version } = target,
log, { sourceUrl, downloadOptions, build } = compiler.options,
targets: [{ version }] url = sourceUrl || `https://nodejs.org/dist/v${version}/node-v${version}.tar.gz`,
} = compiler step = log.step(
const { sourceUrl, downloadOptions } = compiler.options `Downloading ${build ? '' : 'pre-built '}Node.js${build ? `source from: ${url}` : ''}`
const url = sourceUrl || `https://nodejs.org/dist/v${version}/node-v${version}.tar.gz` ),
const step = log.step(`Downloading Node.js source from: ${url}`) exeLocation = compiler.getNodeExecutableLocation(build ? undefined : target),
if (await pathExistsAsync(src)) { downloadExists = await pathExistsAsync(build ? src : exeLocation)
step.log('Source already downloaded')
if (downloadExists) {
step.log('Already downloaded...')
return next() return next()
} }
if (compiler.options.build) { if (build) {
await fetchNodeSourceAsync(src, url, step, downloadOptions) await fetchNodeSourceAsync(src, url, step, downloadOptions)
} else {
await fetchPrebuiltBinary(compiler, step)
} }
return next() return next()
+1 -1
View File
@@ -1,5 +1,5 @@
import { isDirectoryAsync, each } from '../util' import { isDirectoryAsync, each } from '../util'
import * as globs from 'globby' import globs from 'globby'
import { NexeCompiler } from '../compiler' import { NexeCompiler } from '../compiler'
export default async function resource(compiler: NexeCompiler, next: () => Promise<any>) { export default async function resource(compiler: NexeCompiler, next: () => Promise<any>) {
+1
View File
@@ -4,6 +4,7 @@ import pify = require('pify')
import rimraf = require('rimraf') import rimraf = require('rimraf')
const rimrafAsync = pify(rimraf) const rimrafAsync = pify(rimraf)
export const STDIN_FLAG = '[stdin]'
export async function each<T>( export async function each<T>(
list: T[] | Promise<T[]>, list: T[] | Promise<T[]>,
+6 -1
View File
@@ -2,7 +2,7 @@ import * as nexe from '../lib/nexe'
import { getUnBuiltReleases, getLatestGitRelease } from '../lib/releases' import { getUnBuiltReleases, getLatestGitRelease } from '../lib/releases'
import * as ci from './ci' import * as ci from './ci'
import { runDockerBuild } from './docker' import { runDockerBuild } from './docker'
import { getTarget } from '../lib/target' import { getTarget, targetsEqual } from '../lib/target'
import { pathExistsAsync, readFileAsync, execFileAsync } from '../lib/util' import { pathExistsAsync, readFileAsync, execFileAsync } from '../lib/util'
import got = require('got') import got = require('got')
@@ -70,6 +70,11 @@ async function build() {
if (await pathExistsAsync(output)) { if (await pathExistsAsync(output)) {
await assertNexeBinary(output) await assertNexeBinary(output)
const gitRelease = await getLatestGitRelease({ headers }) const gitRelease = await getLatestGitRelease({ headers })
const unbuiltReleases = await getUnBuiltReleases({ headers })
if (!unbuiltReleases.some(x => targetsEqual(x, target))) {
console.log(`${target} already uploaded.`)
return
}
await got(gitRelease.upload_url.split('{')[0], { await got(gitRelease.upload_url.split('{')[0], {
query: { name: target.toString() }, query: { name: target.toString() },
body: await readFileAsync(output), body: await readFileAsync(output),
+4 -4
View File
@@ -9,7 +9,7 @@ function alpine(target: NexeTarget) {
FROM ${target.arch === 'x64' ? '' : 'i386/'}alpine:3.4 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 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 NODE_VERSION=${target.version}
ENV NEXE_VERSION=beta ENV NEXE_VERSION=latest
WORKDIR / WORKDIR /
RUN curl -sSL https://nodejs.org/dist/v\${NODE_VERSION}/node-v\${NODE_VERSION}.tar.gz | tar -xz && \ RUN curl -sSL https://nodejs.org/dist/v\${NODE_VERSION}/node-v\${NODE_VERSION}.tar.gz | tar -xz && \
@@ -21,18 +21,18 @@ RUN curl -sSL https://nodejs.org/dist/v\${NODE_VERSION}/node-v\${NODE_VERSION}.t
RUN rm /nexe_temp/\${NODE_VERSION}/out/Release/node && \ RUN rm /nexe_temp/\${NODE_VERSION}/out/Release/node && \
npm install -g nexe@\${NEXE_VERSION} && \ npm install -g nexe@\${NEXE_VERSION} && \
nexe --build --empty --temp /nexe_temp -c="--fully-static" -o out --enableStdIn=false nexe --build --no-mangle --temp /nexe_temp -c="--fully-static" -o out --enableStdIn=false
`.trim() `.trim()
} }
function arm(target: NexeTarget) { function arm(target: NexeTarget) {
return ` return `
FROM hypriot/rpi-node FROM hypriot/rpi-node
ENV NEXE_VERSION=beta ENV NEXE_VERSION=latest
WORKDIR / WORKDIR /
RUN yarn global add nexe@\${NEXE_VERSION} && \ RUN yarn global add nexe@\${NEXE_VERSION} && \
nexe --build --empty -o out -t ${target.version} nexe --build --no-mangle -o out -t ${target.version}
`.trim() `.trim()
} }
+1 -1
View File
@@ -3,7 +3,7 @@
"target":"es5", "target":"es5",
"skipLibCheck": true, "skipLibCheck": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"lib": ["es2017"], "lib": ["es2018"],
"module": "commonjs", "module": "commonjs",
"outDir": "./lib", "outDir": "./lib",
"strict": true "strict": true