Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b74bdfc53b | |||
| 927820a5ec | |||
| 4ef98bdc8a | |||
| 90d7beb4f3 | |||
| 55c95b4663 | |||
| fe5aa0be09 | |||
| 2a2526d3bc | |||
| c8310ce3bc | |||
| bb356a967b | |||
| 0d62d0100d | |||
| bfd07cd10a | |||
| 4390568654 | |||
| 5c9cf3580c | |||
| fcb54d9085 | |||
| 11fa087e9d | |||
| b822984776 | |||
| 4f2b1c51d5 | |||
| dbcff4c7d2 | |||
| 9906df8d55 | |||
| d685ec21e5 |
@@ -4,6 +4,8 @@ coverage
|
||||
node_modules
|
||||
*.log
|
||||
*.exe
|
||||
*.bin
|
||||
!winsw.exe
|
||||
.idea
|
||||
.nexe
|
||||
lib
|
||||
|
||||
+12
-9
@@ -1,9 +1,12 @@
|
||||
os: osx
|
||||
language: node_js
|
||||
node_js:
|
||||
- '6'
|
||||
script: npm run nexe-build
|
||||
env:
|
||||
- secure: LyC6djYqPfPKz5gHILES9JmSRxQ+ntKOcoaZNM5WID0+gogUoXZBhcmmtvq9RnMpFZN3pYujY6LB5iy7QS2seGjg+feyewNKI30yIK6N9ulJyZZKmrJVDdRx4wvl8YVTkttjcJFkanDGa6zRvFfFDKx/iIdcWLEpnqR/WO1ppf8=
|
||||
notifications:
|
||||
email: false
|
||||
language: node_js
|
||||
before_install:
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get install -y g++-multilib
|
||||
node_js:
|
||||
- '6'
|
||||
script: npm run asset-compile
|
||||
env:
|
||||
global:
|
||||
- secure: LyC6djYqPfPKz5gHILES9JmSRxQ+ntKOcoaZNM5WID0+gogUoXZBhcmmtvq9RnMpFZN3pYujY6LB5iy7QS2seGjg+feyewNKI30yIK6N9ulJyZZKmrJVDdRx4wvl8YVTkttjcJFkanDGa6zRvFfFDKx/iIdcWLEpnqR/WO1ppf8=
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<p align="center">Nexe is a command-line utility that compiles your Node.js application into a single executable file.</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://cloud.githubusercontent.com/assets/5818726/26533446/ce19ee5a-43de-11e7-9540-caf7ebd93370.gif"/>
|
||||
<img src="https://user-images.githubusercontent.com/5818726/30999006-df7e0ae0-a497-11e7-96db-9ce87ae67b34.gif"/>
|
||||
</p>
|
||||
|
||||
## Motivation and Features
|
||||
@@ -37,16 +37,21 @@
|
||||
|
||||
For more CLI options see: `nexe --help`
|
||||
|
||||
# Advanced
|
||||
### Examples
|
||||
|
||||
- `nexe server.js -r public/**/*.html`
|
||||
- `nexe my-bundle.js --no-bundle -o app.exe`
|
||||
- `nexe --build`
|
||||
- `nexe -t x86-8.0.0`
|
||||
|
||||
## Resources
|
||||
|
||||
Additional files or resources can be added to the binary by passing `-r "glob/pattern/**/*"`. These included files can be read in the application by using `fs.readFile` or `fs.readFileSync`
|
||||
Additional files or resources can be added to the binary by passing `-r "glob/pattern/**/*"`. These included files can be read in the application by using `fs.readFile` or `fs.readFileSync`.
|
||||
|
||||
## Compiling Node
|
||||
|
||||
By default `nexe` will attempt to download a pre-built executable. However, It may be unavailable ([github releases](https://github.com/nexe/nexe/releases))
|
||||
or you may want to customize what is built. See `nexe --help` for a list of options available when passing the `--build` option. You will also need to ensure your environment is setup to [build node](https://github.com/nodejs/node/blob/master/BUILDING.md)
|
||||
or you may want to customize what is built. See `nexe --help` for a list of options available when passing the [`--build`](#build-boolean) option. You will also need to ensure your environment is setup to [build node](https://github.com/nodejs/node/blob/master/BUILDING.md). Note: the `python` binary in your path should be an acceptbale version of python 2. eg. Systems that have python2 will need to create a [symlink](https://github.com/nexe/nexe/issues/354#issuecomment-319874486)
|
||||
|
||||
## Node.js API
|
||||
|
||||
@@ -82,14 +87,23 @@ compile({
|
||||
- Output executable file path
|
||||
- default: same as `name` with an OS specific extension.
|
||||
- #### `target: string | object`
|
||||
- Combination of platform-arch-version. e.g. `'win32-ia32-6.10.3'`
|
||||
- An object or string describing platform-arch-version. e.g. `'windows-ia32-6.10.3'`
|
||||
- each segment is optional, and will be merged with the current environment
|
||||
- Examples: ([full list](https://github.com/nexe/nexe/releases))
|
||||
- `'win32-x86-8.6.0`
|
||||
- `{ platform: 'alpine' }`
|
||||
- `darwin-8.6.0`
|
||||
- `linux-x64`
|
||||
- `macos-8.4.0`
|
||||
|
||||
See [test/target.spec.ts](test/target.spec.ts)
|
||||
- If the [`build`](#build-boolean) flag is set, the platform portion of the target is ignored.
|
||||
- default: `process`
|
||||
- #### `bundle: string | boolean`
|
||||
- If a string is provided it must be a valid relative module path
|
||||
and should provide an export with the following signature:
|
||||
```typescript
|
||||
export function createBundle (options: NexeOptions): Promise<string>`
|
||||
export function createBundle (options: NexeOptions): Promise<string>
|
||||
```
|
||||
- default: true
|
||||
- #### `name: string`
|
||||
@@ -99,7 +113,7 @@ compile({
|
||||
- Directory nexe will operate on as though it is the cwd
|
||||
- default: process.cwd()
|
||||
- #### `build: boolean`
|
||||
- Build node from source
|
||||
- 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)
|
||||
- #### `python: string`
|
||||
- On Linux this is the path pointing to your python2 executable
|
||||
- On Windows this is the directory where `python` can be accessed
|
||||
@@ -115,7 +129,7 @@ compile({
|
||||
- #### `make: string[]`
|
||||
- Array of arguments for the node build make step, on windows this step recieves options for vcBuild.bat
|
||||
- default: `[]` or `['nosign', 'release']` for non windows systems
|
||||
- #### `make: string[]`
|
||||
- #### `vcBuild: string[]`
|
||||
- Alias for `make` option
|
||||
- #### `snapshot: string`
|
||||
- path to a file to be used as the warmup snapshot for the build
|
||||
@@ -132,7 +146,15 @@ compile({
|
||||
- Path to a user provided icon to be used (Windows only).
|
||||
- #### `rc: object`
|
||||
- Settings for patching the [node.rc](https://github.com/nodejs/node/blob/master/src/res/node.rc) configuration file (Windows only).
|
||||
- Example: `{ CompanyName: "ACME Corp" }`
|
||||
- Example:
|
||||
```javascript
|
||||
{
|
||||
CompanyName: "ACME Corp",
|
||||
PRODUCTVERSION: "17,3,0,0",
|
||||
FILEVERSION: "1,2,3,4"
|
||||
...
|
||||
}
|
||||
```
|
||||
- default: `{}`
|
||||
- #### `clean: boolean`
|
||||
- If included, nexe will remove temporary files for the accompanying configuration and exit
|
||||
@@ -150,12 +172,15 @@ compile({
|
||||
- #### `patches: NexePatch[]`
|
||||
- Userland patches for patching or modifying node source
|
||||
- default: `[]`
|
||||
- #### `plugins: NexePatch[]`
|
||||
- Userland plugins for modifying nexe executable behavior
|
||||
- default: `[]`
|
||||
|
||||
### `NexePatch: (compiler: NexeCompiler, next: () => Promise<void>) => Promise<void>`
|
||||
|
||||
A patch is just a middleware function that takes two arguments, the `compiler`, and `next`. The compiler is described below, and `next` ensures that the pipeline continues. Its invocation should always be awaited or returned to ensure correct behavior.
|
||||
Patches and Plugins are just a middleware functions that take two arguments, the `compiler`, and `next`. The compiler is described below, and `next` ensures that the pipeline continues. Its invocation should always be awaited or returned to ensure correct behavior. Patches also require that [`--build`](#build-boolean) be set, while plugins do not.
|
||||
|
||||
For examples, see the built in patches: [src/patches](src/patches)
|
||||
For examples, see the built in patches: [src/patches](src/patches).
|
||||
|
||||
### `NexeCompiler`
|
||||
|
||||
@@ -165,6 +190,8 @@ For examples, see the built in patches: [src/patches](src/patches)
|
||||
- Quickly perform a replace in a file within the downloaded Node.js source. The rest arguments are passed along to `String.prototype.replace`
|
||||
- `readFileAsync(filename: string): Promise<NexeFile>`
|
||||
- Access (or create) a file within the downloaded Node.js source.
|
||||
- `addResource(filename: string, contents: Buffer): void`
|
||||
- Add a resource to the nexe bundle
|
||||
- `files: NexeFile[]`
|
||||
- The cache of the currently read, modified, or created files within the downloaded Node.js source.
|
||||
|
||||
@@ -177,10 +204,29 @@ Any modifications made to `NexeFile#contents` will be maintained in the cache _w
|
||||
|
||||
## Native Modules
|
||||
|
||||
Nexe has a plugin built for use with [fuse-box](http://fuse-box.org) > 2.2.1. This plugin currently supports modules that require `.node` files and those that use the `bindings` module.
|
||||
Take a look at the (windows) [example](examples/native-build/build.js)
|
||||
Any `.node` binding can be used with nexe. These library files will be bundled and written alongside the resulting binary at runtime. Currently, nexe supports modules loaded directly (`.node` files) and those loaded with the `'bindings'` module. It does not yet support modules using `node-pre-gyp#find`.
|
||||
|
||||
- [ ] Implement support `node-pre-gyp#find`.
|
||||
Its important to note: unless your native module conditionally loads each platform binary. Nexe builds with native modules will be platform specific. Eg. You will no longer be able to use cross platform builds.
|
||||
|
||||
Nexe builds with native modules will need to target the same version, platform and architecture as the platform hosting the module. At least until [N-API](https://github.com/nodejs/abi-stable-node) is fully propegated
|
||||
|
||||
TODO
|
||||
|
||||
- [ ] Implement support for `node-pre-gyp#find`.
|
||||
|
||||
## Contributing
|
||||
|
||||
Building
|
||||
```
|
||||
$ git clone git@github.com:nexe/nexe.git
|
||||
$ cd nexe
|
||||
$ yarn
|
||||
```
|
||||
|
||||
Testing
|
||||
```
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## Maintainers
|
||||
|
||||
|
||||
+2
-5
@@ -5,16 +5,13 @@ environment:
|
||||
secure: tR7HXYv3x97q90uvPrjUxKZh7R1+uqKct0HdEcqTbsg=
|
||||
GITHUB_TOKEN:
|
||||
secure: 3wuEGJsppSKFvdwhp3jprUA9Zkc3WINlc/9oFXjhAR6J05lYMXP7Fl7lXTKpkGZx
|
||||
branches:
|
||||
except:
|
||||
- master
|
||||
install:
|
||||
- ps: Install-Product node 6
|
||||
- npm i
|
||||
- yarn
|
||||
build: off
|
||||
test: off
|
||||
build_script:
|
||||
- npm run nexe-build
|
||||
- yarn run asset-compile
|
||||
artifacts:
|
||||
- path: .nexe\**\node.exe
|
||||
cache:
|
||||
|
||||
+1
-6
@@ -1,8 +1,3 @@
|
||||
machine:
|
||||
node:
|
||||
version: 6.11.2
|
||||
services:
|
||||
- docker
|
||||
test:
|
||||
override:
|
||||
- npm run nexe-build
|
||||
- npm run asset-compile
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
const nexe = require('./lib/nexe')
|
||||
const eol = require('os').EOL
|
||||
|
||||
module.exports = nexe
|
||||
|
||||
const options = require('./lib/options')
|
||||
if (require.main === module) {
|
||||
nexe.compile(nexe.argv).catch((e) => {
|
||||
process.stderr.write(eol + e.stack, () => process.exit(1))
|
||||
})
|
||||
//fast path for help/version
|
||||
const argv = options.argv
|
||||
const eol = require('os').EOL
|
||||
const showHelp = argv.help || argv._.some(x => x === 'help')
|
||||
const showVersion = argv.version || argv._.some(x => x === 'version')
|
||||
if (showHelp || showVersion) {
|
||||
process.stderr.write(showHelp ? options.help : options.version + eol)
|
||||
} else {
|
||||
const nexe = require('./lib/nexe')
|
||||
nexe.compile(argv).catch((e) => {
|
||||
process.stderr.write(eol + e.stack)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
module.exports = require('./lib/nexe')
|
||||
}
|
||||
|
||||
Generated
-2995
File diff suppressed because it is too large
Load Diff
+8
-4
@@ -2,19 +2,20 @@
|
||||
"name": "nexe",
|
||||
"description": "Create a single executable out of your Node.js application",
|
||||
"license": "MIT",
|
||||
"version": "2.0.0-rc.5",
|
||||
"version": "2.0.0-rc.12",
|
||||
"contributors": [
|
||||
"Craig Condon <craig.j.condon@gmail.com> (http://crcn.io)",
|
||||
"Jared Allard <jaredallard@outlook.com>",
|
||||
"Caleb Boyd <caleb.boyd@hotmail.com>"
|
||||
],
|
||||
"scripts": {
|
||||
"nexe-build": "ts-node tasks/build",
|
||||
"asset-compile": "ts-node tasks/asset-compile",
|
||||
"prebuild": "rimraf lib && npm run lint",
|
||||
"prepublish": "npm test && npm run build",
|
||||
"test": "mocha test/**/*.spec.ts",
|
||||
"lint": "prettier --parser typescript --no-semi --print-width 100 --single-quote --write \"src/**/*.ts\"",
|
||||
"build": "tsc --declaration"
|
||||
"build": "tsc --declaration",
|
||||
"postbuild": "ts-node tasks/post-build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -24,6 +25,7 @@
|
||||
"lib",
|
||||
"source-map-support.js"
|
||||
],
|
||||
"typings": "lib/nexe.d.ts",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"nexe": "index.js"
|
||||
@@ -35,7 +37,7 @@
|
||||
"app-builder": "^5.1.0",
|
||||
"chalk": "^1.1.3",
|
||||
"download": "^6.2.0",
|
||||
"fuse-box": "2.2.31",
|
||||
"fuse-box": "2.3.1-beta.23",
|
||||
"globby": "^6.1.0",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
@@ -47,6 +49,7 @@
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.0.4",
|
||||
"@types/chalk": "^0.4.31",
|
||||
"@types/execa": "^0.7.0",
|
||||
"@types/globby": "^0.6.0",
|
||||
"@types/minimist": "^1.2.0",
|
||||
"@types/mkdirp": "^0.3.29",
|
||||
@@ -55,6 +58,7 @@
|
||||
"@types/pify": "0.0.28",
|
||||
"@types/rimraf": "0.0.28",
|
||||
"chai": "^4.1.2",
|
||||
"execa": "^0.8.0",
|
||||
"got": "^7.1.0",
|
||||
"mocha": "^3.5.0",
|
||||
"prettier": "^1.6.1",
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "nexe-daemon",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin for nexe to create installable services",
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"clean": "rimraf *.js",
|
||||
"build": "tsc",
|
||||
"postpublish": "npm run clean"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"winsw.exe"
|
||||
],
|
||||
"author": "Caleb Boyd",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"rimraf": "^2.6.2",
|
||||
"typescript": "^2.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"nexe": "^2.0.0-rc.11"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { NexeCompiler, NexeOptions } from 'nexe'
|
||||
import { readFileSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
|
||||
interface NexeDaemonOptions {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
executable: string
|
||||
}
|
||||
|
||||
type DaemonOptions = NexeOptions & { daemon: { windows: NexeDaemonOptions } }
|
||||
|
||||
function renderWinswConfig(options: any) {
|
||||
return '<configuration>\r\n' +
|
||||
`${Object.keys(options).reduce((config: string, element: string) => {
|
||||
return config += `<${element}>${options[element]}</${element}>\r\n`
|
||||
}, '')}</configuration>\r\n`
|
||||
}
|
||||
|
||||
export default function daemon (compiler: NexeCompiler<DaemonOptions>, next: () => Promise<void>) {
|
||||
if (compiler.target.platform !== 'windows') {
|
||||
return next()
|
||||
}
|
||||
compiler.addResource(
|
||||
'./nexe/plugin/daemon/winsw.exe',
|
||||
readFileSync(require.resolve('../winsw.exe'))
|
||||
)
|
||||
const name = compiler.options.name,
|
||||
options = compiler.options.daemon && compiler.options.daemon.windows || {},
|
||||
defaults: NexeDaemonOptions = {
|
||||
id: name,
|
||||
name,
|
||||
description: name,
|
||||
executable: '%BASE%\\' + compiler.output
|
||||
}
|
||||
|
||||
compiler.addResource(
|
||||
'./nexe/plugin/daemon/winsw.xml',
|
||||
Buffer.from(renderWinswConfig(Object.assign(defaults, options)))
|
||||
)
|
||||
compiler.addResource(
|
||||
'./nexe/plugin/daemon/app.js',
|
||||
Buffer.from(compiler.input)
|
||||
)
|
||||
compiler.input = '{{replace:plugins/nexe-daemon/lib/nexe-daemon.js}}'
|
||||
|
||||
return next()
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
const installCommands = ['-i', '--install']
|
||||
const installIndex = process.argv.findIndex(x => installCommands.includes(x))
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
import * as cp from 'child_process'
|
||||
|
||||
function mkdirp (r: string, t?: any): any {
|
||||
;(t = t || null), (r = path.resolve(r))
|
||||
try {
|
||||
fs.mkdirSync(r), (t = t || r)
|
||||
} catch (c) {
|
||||
if ('ENOENT' === c.code) (t = mkdirp(path.dirname(r), t)), mkdirp(r, t)
|
||||
else {
|
||||
var i
|
||||
try {
|
||||
i = fs.statSync(r)
|
||||
} catch (r) {
|
||||
throw c
|
||||
}
|
||||
if (!i.isDirectory()) throw c
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
if (~installIndex) {
|
||||
console.log('DIRECTORY FOUND')
|
||||
const directory = process.argv[installIndex + 1]
|
||||
if (directory) {
|
||||
mkdirp(path.dirname(directory))
|
||||
const filename = path.join(directory, path.basename(process.execPath))
|
||||
const readStream = fs.createReadStream(process.execPath)
|
||||
const writeStream = fs.createWriteStream(filename)
|
||||
|
||||
const onError = function () {
|
||||
readStream.removeAllListeners()
|
||||
writeStream.removeAllListeners()
|
||||
//TODO
|
||||
}
|
||||
|
||||
readStream
|
||||
.on('error', onError)
|
||||
.pipe(writeStream)
|
||||
.on('error', onError)
|
||||
.on('close', () => {
|
||||
const winsw = filename.replace('.exe', '-service.exe')
|
||||
fs.writeFileSync(
|
||||
winsw,
|
||||
fs.readFileSync('./nexe/plugin/daemon/winsw.exe')
|
||||
)
|
||||
fs.writeFileSync(
|
||||
filename.replace('.exe', '-service.xml'),
|
||||
fs.readFileSync('./nexe/plugin/daemon/winsw.xml')
|
||||
)
|
||||
installService(winsw)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
console.log("DIRECTORY NOT FOUND")
|
||||
require('./nexe/plugin/daemon/app.js')
|
||||
}
|
||||
|
||||
function installService (filename: string) {
|
||||
cp.exec(filename + ' install', (error) => {
|
||||
if (error && !error.message.includes('already exists')) {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"lib": [ "es2016" ],
|
||||
"outDir": "lib",
|
||||
"strict": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": []
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
+74
-49
@@ -5,8 +5,8 @@ import { createReadStream } from 'fs'
|
||||
import { Readable } from 'stream'
|
||||
import { spawn } from 'child_process'
|
||||
import { Logger } from './logger'
|
||||
import { readFileAsync, writeFileAsync, pathExistsAsync, dequote, isWindows } from './util'
|
||||
import { NexeOptions, nexeVersion } from './options'
|
||||
import { readFileAsync, writeFileAsync, pathExistsAsync, dequote, isWindows, bound } from './util'
|
||||
import { NexeOptions, version } from './options'
|
||||
import { NexeTarget } from './target'
|
||||
import download = require('download')
|
||||
import { getLatestGitRelease } from './releases'
|
||||
@@ -22,34 +22,35 @@ export interface NexeFile {
|
||||
contents: string
|
||||
}
|
||||
|
||||
export { NexeOptions }
|
||||
|
||||
interface NexeHeader {
|
||||
resources: { [key: string]: number[] }
|
||||
version: string
|
||||
}
|
||||
|
||||
export class NexeCompiler {
|
||||
export class NexeCompiler<T extends NexeOptions = NexeOptions> {
|
||||
private start = Date.now()
|
||||
private env = { ...process.env }
|
||||
private compileStep: { modify: Function; log: Function }
|
||||
public log = new Logger(this.options.loglevel)
|
||||
public src: string
|
||||
public files: NexeFile[] = []
|
||||
public shims: string[] = []
|
||||
public input: string
|
||||
public bundledInput?: string
|
||||
public output: string | null
|
||||
public targets: NexeTarget[]
|
||||
public target: NexeTarget
|
||||
public resources: { bundle: string; index: { [key: string]: number[] } } = {
|
||||
public resources: { bundle: Buffer; index: { [key: string]: number[] } } = {
|
||||
index: {},
|
||||
bundle: ''
|
||||
bundle: Buffer.from('')
|
||||
}
|
||||
public readFileAsync: (file: string) => Promise<NexeFile>
|
||||
public writeFileAsync: (file: string, contents: Buffer | string) => Promise<void>
|
||||
public replaceInFileAsync: (file: string, replacer: any, replaceValue: string) => Promise<void>
|
||||
public setFileContentsAsync: (file: string, contents: string | Buffer) => Promise<void>
|
||||
private nodeSrcBinPath: string
|
||||
public output = isWindows
|
||||
? `${(this.options.output || this.options.name).replace(/\.exe$/, '')}.exe`
|
||||
: `${this.options.output || this.options.name}`
|
||||
|
||||
constructor(public options: NexeOptions) {
|
||||
private nodeSrcBinPath: string
|
||||
constructor(public options: T) {
|
||||
const { python } = (this.options = options)
|
||||
this.targets = options.targets as NexeTarget[]
|
||||
this.target = this.targets[0]
|
||||
@@ -57,7 +58,7 @@ export class NexeCompiler {
|
||||
this.nodeSrcBinPath = isWindows
|
||||
? join(this.src, 'Release', 'node.exe')
|
||||
: join(this.src, 'out', 'Release', 'node')
|
||||
this.log.step('nexe ' + nexeVersion, 'info')
|
||||
this.log.step('nexe ' + version, 'info')
|
||||
if (python) {
|
||||
if (isWindows) {
|
||||
this.env.PATH = '"' + dequote(normalize(python)) + '";' + this.env.PATH
|
||||
@@ -65,40 +66,64 @@ export class NexeCompiler {
|
||||
this.env.PYTHON = python
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.readFileAsync = async (file: string) => {
|
||||
let cachedFile = this.files.find(x => normalize(x.filename) === normalize(file))
|
||||
if (!cachedFile) {
|
||||
const absPath = join(this.src, file)
|
||||
cachedFile = {
|
||||
absPath,
|
||||
filename: file,
|
||||
contents: await readFileAsync(absPath, 'utf-8').catch(x => {
|
||||
if (x.code === 'ENOENT') return ''
|
||||
throw x
|
||||
})
|
||||
}
|
||||
this.files.push(cachedFile)
|
||||
@bound
|
||||
addResource(file: string, contents: Buffer) {
|
||||
const { resources } = this
|
||||
resources.index[file] = [resources.bundle.byteLength, contents.byteLength]
|
||||
resources.bundle = Buffer.concat([resources.bundle, contents])
|
||||
}
|
||||
|
||||
@bound
|
||||
async readFileAsync(file: string) {
|
||||
this.assertBuild()
|
||||
let cachedFile = this.files.find(x => normalize(x.filename) === normalize(file))
|
||||
if (!cachedFile) {
|
||||
const absPath = join(this.src, file)
|
||||
cachedFile = {
|
||||
absPath,
|
||||
filename: file,
|
||||
contents: await readFileAsync(absPath, 'utf-8').catch(x => {
|
||||
if (x.code === 'ENOENT') return ''
|
||||
throw x
|
||||
})
|
||||
}
|
||||
return cachedFile
|
||||
}
|
||||
this.writeFileAsync = (file, contents) => writeFileAsync(join(this.src, file), contents)
|
||||
this.replaceInFileAsync = async (file, replace: string | RegExp, value: string) => {
|
||||
const entry = await this.readFileAsync(file)
|
||||
entry.contents = entry.contents.replace(replace, value)
|
||||
}
|
||||
this.setFileContentsAsync = async (file: string, contents: string) => {
|
||||
const entry = await this.readFileAsync(file)
|
||||
entry.contents = contents
|
||||
this.files.push(cachedFile)
|
||||
}
|
||||
return cachedFile
|
||||
}
|
||||
|
||||
@bound
|
||||
writeFileAsync(file: string, contents: string | Buffer) {
|
||||
this.assertBuild()
|
||||
return writeFileAsync(join(this.src, file), contents)
|
||||
}
|
||||
|
||||
@bound
|
||||
async replaceInFileAsync(file: string, replace: string | RegExp, value: string) {
|
||||
const entry = await this.readFileAsync(file)
|
||||
entry.contents = entry.contents.replace(replace, value)
|
||||
}
|
||||
|
||||
@bound
|
||||
async setFileContentsAsync(file: string, contents: string) {
|
||||
const entry = await this.readFileAsync(file)
|
||||
entry.contents = contents
|
||||
}
|
||||
|
||||
quit() {
|
||||
const time = Date.now() - this.start
|
||||
this.log.write(`Finsihed in ${time / 1000}s`)
|
||||
this.log.write(`Finished in ${time / 1000}s`)
|
||||
return this.log.flush()
|
||||
}
|
||||
|
||||
assertBuild() {
|
||||
if (!this.options.build) {
|
||||
throw new Error('This feature is only available with `--build`')
|
||||
}
|
||||
}
|
||||
|
||||
public getNodeExecutableLocation(target?: NexeTarget) {
|
||||
if (target) {
|
||||
return join(this.options.temp, target.toString())
|
||||
@@ -114,7 +139,13 @@ export class NexeCompiler {
|
||||
stdio: 'ignore'
|
||||
})
|
||||
.once('error', reject)
|
||||
.once('close', resolve)
|
||||
.once('close', (code: number) => {
|
||||
if (code != 0) {
|
||||
const error = `${command} ${args.join(' ')} exited with code: ${code}`
|
||||
reject(new Error(error))
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -169,7 +200,7 @@ export class NexeCompiler {
|
||||
return createReadStream(filename)
|
||||
}
|
||||
|
||||
private _generateHeader() {
|
||||
getHeader() {
|
||||
const version =
|
||||
['configure', 'vcBuild', 'make'].reduce((a, c) => {
|
||||
return (a += (this.options as any)[c]
|
||||
@@ -183,11 +214,6 @@ export class NexeCompiler {
|
||||
.update(version)
|
||||
.digest('hex')
|
||||
}
|
||||
const serializedHeader = this._serializeHeader(header)
|
||||
return header
|
||||
}
|
||||
|
||||
private _serializeHeader(header: NexeHeader) {
|
||||
return `process.__nexe=${JSON.stringify(header)};`
|
||||
}
|
||||
|
||||
@@ -196,7 +222,6 @@ export class NexeCompiler {
|
||||
const build = this.options.build
|
||||
const location = this.getNodeExecutableLocation(build ? undefined : target)
|
||||
let binary = (await pathExistsAsync(location)) ? createReadStream(location) : null
|
||||
const header = this._generateHeader()
|
||||
if (!build && !binary) {
|
||||
step.modify('Fetching prebuilt binary')
|
||||
binary = await this._fetchPrebuiltBinaryAsync(target)
|
||||
@@ -205,10 +230,10 @@ export class NexeCompiler {
|
||||
binary = await this._buildAsync()
|
||||
step.log('Node binary compiled')
|
||||
}
|
||||
return this._assembleDeliverable(header, binary)
|
||||
return this._assembleDeliverable(binary)
|
||||
}
|
||||
|
||||
private _assembleDeliverable(header: NexeHeader, binary: NodeJS.ReadableStream) {
|
||||
private _assembleDeliverable(binary: NodeJS.ReadableStream) {
|
||||
if (this.options.empty) {
|
||||
return binary
|
||||
}
|
||||
@@ -217,12 +242,12 @@ export class NexeCompiler {
|
||||
artifact.push(chunk)
|
||||
})
|
||||
binary.on('close', () => {
|
||||
const content = this._serializeHeader(header) + this.input
|
||||
const content = [this.shims.join(''), this.input].join(';')
|
||||
artifact.push(content)
|
||||
artifact.push(this.resources.bundle)
|
||||
const lengths = Buffer.from(Array(16))
|
||||
lengths.writeDoubleLE(Buffer.byteLength(content), 0)
|
||||
lengths.writeDoubleLE(Buffer.byteLength(this.resources.bundle), 8)
|
||||
lengths.writeDoubleLE(this.resources.bundle.byteLength, 8)
|
||||
artifact.push(Buffer.concat([Buffer.from('<nexe~~sentinel>'), lengths]))
|
||||
artifact.push(null)
|
||||
})
|
||||
|
||||
+4
-10
@@ -1,13 +1,7 @@
|
||||
import { compose, PromiseConfig, Middleware } from 'app-builder'
|
||||
import { compose, Middleware } from 'app-builder'
|
||||
import resource from './steps/resource'
|
||||
import { NexeCompiler } from './compiler'
|
||||
import {
|
||||
argv,
|
||||
nexeVersion as version,
|
||||
normalizeOptionsAsync,
|
||||
NexeOptions,
|
||||
NexePatch
|
||||
} from './options'
|
||||
import { argv, version, help, normalizeOptionsAsync, NexeOptions, NexePatch } from './options'
|
||||
import cli from './steps/cli'
|
||||
import bundle from './steps/bundle'
|
||||
import download from './steps/download'
|
||||
@@ -40,7 +34,7 @@ async function compile(
|
||||
const buildSteps = build
|
||||
? [download, artifacts, ...patches, ...(options.patches as NexePatch[])]
|
||||
: []
|
||||
const nexe = compose(resource, bundle, cli, buildSteps, shim)
|
||||
const nexe = compose(resource, bundle, cli, buildSteps, shim, options.plugins as NexePatch[])
|
||||
return callback
|
||||
? void nexe(compiler).then(
|
||||
() => callback && callback(null),
|
||||
@@ -52,4 +46,4 @@ async function compile(
|
||||
: nexe(compiler)
|
||||
}
|
||||
|
||||
export { argv, compile, version }
|
||||
export { argv, compile, version, NexeCompiler, NexeOptions, help }
|
||||
|
||||
+51
-43
@@ -6,7 +6,7 @@ import { getTarget, NexeTarget } from './target'
|
||||
import { EOL } from 'os'
|
||||
import * as c from 'chalk'
|
||||
|
||||
export const nexeVersion = '2.0.0-rc.5'
|
||||
export const version = '{{replace:0}}'
|
||||
|
||||
export interface NexePatch {
|
||||
(compiler: NexeCompiler, next: () => Promise<void>): Promise<void>
|
||||
@@ -30,9 +30,11 @@ export interface NexeOptions {
|
||||
enableNodeCli: boolean
|
||||
bundle: boolean | string
|
||||
patches: (string | NexePatch)[]
|
||||
plugins: (string | NexePatch)[]
|
||||
native: any
|
||||
empty: boolean
|
||||
sourceUrl?: string
|
||||
enableStdIn?: boolean
|
||||
python?: string
|
||||
loglevel: 'info' | 'silent' | 'verbose'
|
||||
silent?: boolean
|
||||
@@ -61,7 +63,8 @@ const defaults = {
|
||||
compress: false,
|
||||
build: false,
|
||||
bundle: true,
|
||||
patches: []
|
||||
patches: [],
|
||||
plugins: []
|
||||
}
|
||||
const alias = {
|
||||
i: 'input',
|
||||
@@ -80,44 +83,44 @@ const alias = {
|
||||
l: 'loglevel',
|
||||
'fake-argv': 'fakeArgv'
|
||||
}
|
||||
const argv = parseArgv(process.argv, { alias, default: defaults })
|
||||
const argv = parseArgv(process.argv, { alias, default: { ...defaults, enableStdIn: true } })
|
||||
const g = c.gray
|
||||
let help = `
|
||||
${c.bold('nexe <entry-file> [options]')}
|
||||
|
||||
${c.underline.bold('Options:')}
|
||||
|
||||
-i --input ${g('=index.js')} -- application entry point
|
||||
-o --output ${g('=my-app.exe')} -- path to output file
|
||||
-t --target ${g('=8.4.0-x64')} -- node version description
|
||||
-n --name ${g('=my-app')} -- main app module name
|
||||
|
||||
-r --resource -- *embed files (glob) within the binary
|
||||
-i --input -- application entry point
|
||||
-o --output -- path to output file
|
||||
-t --target -- node version description
|
||||
-n --name -- main app module name
|
||||
-r --resource -- *embed files (glob) within the binary
|
||||
--plugin -- extend nexe runtime behavior
|
||||
|
||||
${c.underline.bold('Building from source:')}
|
||||
|
||||
-b --build -- build from source
|
||||
-p --python -- python2 (as python) executable path
|
||||
-f --flag -- *v8 flags to include during compilation
|
||||
-c --configure -- *arguments to the configure step
|
||||
-m --make -- *arguments to the make/build step
|
||||
--snapshot -- path to a warmup snapshot
|
||||
--ico -- file name for alternate icon file (windows)
|
||||
--rc-* -- populate rc file options (windows)
|
||||
--sourceUrl -- pass an alternate source (node.tar.gz) url
|
||||
--enableNodeCli -- enable node cli enforcement (blocks app cli)
|
||||
-b --build -- build from source
|
||||
-p --python -- python2 (as python) executable path
|
||||
-f --flag -- *v8 flags to include during compilation
|
||||
-c --configure -- *arguments to the configure step
|
||||
-m --make -- *arguments to the make/build step
|
||||
--snapshot -- path to a warmup snapshot
|
||||
--ico -- file name for alternate icon file (windows)
|
||||
--rc-* -- populate rc file options (windows)
|
||||
--sourceUrl -- pass an alternate source (node.tar.gz) url
|
||||
--enableNodeCli -- enable node cli enforcement (blocks app cli)
|
||||
|
||||
${c.underline.bold('Other options:')}
|
||||
|
||||
--bundle -- custom bundling module with 'createBundle' export
|
||||
--temp -- temp file storage default './nexe'
|
||||
--cwd -- set the current working directory for the command
|
||||
--fake-argv TODO -- fake argv[1] with entry file
|
||||
--clean -- force download of sources
|
||||
--silent -- disable logging
|
||||
--verbose -- set logging to verbose
|
||||
--bundle -- custom bundling module with 'createBundle' export
|
||||
--temp -- temp file storage default './nexe'
|
||||
--cwd -- set the current working directory for the command
|
||||
--fake-argv -- fake argv[1] with entry file
|
||||
--clean -- force download of sources
|
||||
--silent -- disable logging
|
||||
--verbose -- set logging to verbose
|
||||
|
||||
-* variable key name * option can be used more than once`.trim()
|
||||
-* variable key name * option can be used more than once`.trim()
|
||||
help = EOL + help + EOL
|
||||
|
||||
function flatten(...args: any[]): string[] {
|
||||
@@ -148,10 +151,6 @@ function tryResolveMainFileName(cwd: string) {
|
||||
filename = basename(file).replace(extname(file), '')
|
||||
} catch (_) {}
|
||||
|
||||
if (filename === 'index' && basename(cwd)) {
|
||||
return basename(cwd)
|
||||
}
|
||||
|
||||
return filename ? filename : 'nexe_' + Date.now()
|
||||
}
|
||||
|
||||
@@ -162,12 +161,23 @@ function extractLogLevel(options: NexeOptions) {
|
||||
return 'info'
|
||||
}
|
||||
|
||||
function isName(name: string) {
|
||||
return name && name !== 'index'
|
||||
}
|
||||
|
||||
function extractName(options: NexeOptions) {
|
||||
let name = options.name
|
||||
if (!name && typeof options.input === 'string') {
|
||||
if (!isName(name) && typeof options.input === 'string') {
|
||||
name = basename(options.input).replace(extname(options.input), '')
|
||||
}
|
||||
name = name === 'index' ? tryResolveMainFileName(options.cwd) : name
|
||||
|
||||
if (!isName(name)) {
|
||||
name = tryResolveMainFileName(options.cwd)
|
||||
}
|
||||
|
||||
if (!isName(name) && basename(options.cwd)) {
|
||||
name = basename(options.cwd)
|
||||
}
|
||||
|
||||
return name.replace(/\.exe$/, '')
|
||||
}
|
||||
@@ -196,16 +206,11 @@ function findInput(input: string, cwd: string) {
|
||||
return ''
|
||||
}
|
||||
|
||||
function normalizeOptionsAsync(input?: Partial<NexeOptions>): Promise<NexeOptions | never> {
|
||||
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))
|
||||
})
|
||||
}
|
||||
function normalizeOptionsAsync(input?: Partial<NexeOptions>): Promise<NexeOptions> {
|
||||
const options = Object.assign({}, defaults, input) as NexeOptions
|
||||
const opts = options as any
|
||||
|
||||
options.temp = process.env.NEXE_TEMP || join(options.cwd, '.nexe')
|
||||
options.temp = options.temp || process.env.NEXE_TEMP || join(options.cwd, '.nexe')
|
||||
options.input = findInput(options.input, options.cwd)
|
||||
options.name = extractName(options)
|
||||
options.loglevel = extractLogLevel(options)
|
||||
@@ -229,12 +234,15 @@ function normalizeOptionsAsync(input?: Partial<NexeOptions>): Promise<NexeOption
|
||||
}
|
||||
}
|
||||
|
||||
options.patches = options.patches.map(x => {
|
||||
const requireDefault = (x: string) => {
|
||||
if (typeof x === 'string') {
|
||||
return require(x).default
|
||||
}
|
||||
return x
|
||||
})
|
||||
}
|
||||
|
||||
options.plugins = flatten(opts.plugin, options.plugins).map(requireDefault)
|
||||
options.patches = flatten(opts.patch, options.patches).map(requireDefault)
|
||||
|
||||
Object.keys(alias)
|
||||
.filter(k => k !== 'rc')
|
||||
@@ -243,4 +251,4 @@ function normalizeOptionsAsync(input?: Partial<NexeOptions>): Promise<NexeOption
|
||||
return Promise.resolve(options)
|
||||
}
|
||||
|
||||
export { argv, normalizeOptionsAsync }
|
||||
export { argv, normalizeOptionsAsync, help }
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
const fs = require('fs')
|
||||
const fd = fs.openSync(process.execPath, 'r')
|
||||
const stat = fs.statSync(process.execPath)
|
||||
const footer = Buffer.from(Array(32))
|
||||
|
||||
fs.readSync(fd, footer, 0, 32, stat.size - 32)
|
||||
|
||||
if (!footer.slice(0, 16).equals(Buffer.from('<nexe~~sentinel>'))) {
|
||||
throw 'Invalid Nexe binary'
|
||||
}
|
||||
|
||||
const contentSize = footer.readDoubleLE(16)
|
||||
const resourceSize = footer.readDoubleLE(24)
|
||||
const contentStart = stat.size - 32 - resourceSize - contentSize
|
||||
const resourceStart = contentStart + contentSize
|
||||
|
||||
Object.defineProperty(
|
||||
process,
|
||||
'__nexe',
|
||||
(function() {
|
||||
let nexeHeader: any = null
|
||||
return {
|
||||
get: function() {
|
||||
return nexeHeader
|
||||
},
|
||||
set: function(value: any) {
|
||||
if (nexeHeader) {
|
||||
throw new Error('__nexe cannot be reconfigured')
|
||||
}
|
||||
nexeHeader = Object.assign({}, value, {
|
||||
layout: {
|
||||
stat,
|
||||
contentSize,
|
||||
contentStart,
|
||||
resourceSize,
|
||||
resourceStart
|
||||
}
|
||||
})
|
||||
Object.freeze(nexeHeader)
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: false
|
||||
}
|
||||
})()
|
||||
)
|
||||
|
||||
const contentBuffer = Buffer.from(Array(contentSize))
|
||||
fs.readSync(fd, contentBuffer, 0, contentSize, contentStart)
|
||||
fs.closeSync(fd)
|
||||
const Module = require('module')
|
||||
process.mainModule = new Module(process.execPath, null)
|
||||
process.mainModule!.loaded = true
|
||||
;(process.mainModule as any)._compile(contentBuffer.toString(), process.execPath)
|
||||
@@ -5,7 +5,7 @@ import cli from './disable-node-cli'
|
||||
import flags from './flags'
|
||||
import ico from './ico'
|
||||
import rc from './node-rc'
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { NexeCompiler, NexeOptions } from '../compiler'
|
||||
|
||||
const patches = [gyp, nexePatches, buildFixes, cli, flags, ico, rc]
|
||||
|
||||
|
||||
@@ -5,122 +5,7 @@ import { join } from 'path'
|
||||
export default async function main(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
await compiler.setFileContentsAsync(
|
||||
'lib/_third_party_main.js',
|
||||
`
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const Buffer = require('buffer').Buffer
|
||||
const isString = x => typeof x === 'string' || x instanceof String
|
||||
|
||||
const fd = fs.openSync(process.execPath, 'r')
|
||||
const size = fs.statSync(process.execPath).size
|
||||
const footer = Buffer.from(Array(32))
|
||||
fs.readSync(fd, footer, 0, 32, size - 32)
|
||||
|
||||
if (!footer.slice(0, 16).equals(Buffer.from('<nexe~~sentinel>'))) {
|
||||
throw 'Invalid Nexe binary'
|
||||
}
|
||||
|
||||
const contentSize = footer.readDoubleLE(16)
|
||||
const resourceSize = footer.readDoubleLE(24)
|
||||
const contentStart = size - 32 - resourceSize - contentSize
|
||||
const resourceStart = contentStart + contentSize
|
||||
|
||||
Object.defineProperty(process, '__nexe', (function () {
|
||||
let nexeHeader = null
|
||||
return {
|
||||
get: function () {
|
||||
return nexeHeader
|
||||
},
|
||||
set: function (value) {
|
||||
if (nexeHeader) {
|
||||
throw new Error('__nexe cannot be reconfigured')
|
||||
}
|
||||
nexeHeader = value
|
||||
Object.freeze(nexeHeader)
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: false
|
||||
}
|
||||
})());
|
||||
|
||||
if (resourceSize) {
|
||||
const originalReadFile = fs.readFile
|
||||
const originalReadFileSync = fs.readFileSync
|
||||
let setupManifest = () => {
|
||||
const manifest = process.__nexe && process.__nexe.resources
|
||||
if (!manifest) {
|
||||
return
|
||||
}
|
||||
Object.keys(manifest).forEach((key) => {
|
||||
const absolutePath = path.resolve(key)
|
||||
if (!manifest[absolutePath]) {
|
||||
manifest[absolutePath] = manifest[key]
|
||||
}
|
||||
const normalizedPath = path.normalize(key)
|
||||
if (!manifest[normalizedPath]) {
|
||||
manifest[normalizedPath] = manifest[key]
|
||||
}
|
||||
})
|
||||
normalizeManifest = () => {}
|
||||
};
|
||||
fs.readFile = function readFile (file, options, callback) {
|
||||
setupManifest()
|
||||
const manifest = process.__nexe
|
||||
const entry = manifest && manifest.resources[file]
|
||||
if (!manifest || !entry || !isString(file)) {
|
||||
return originalReadFile.apply(fs, arguments)
|
||||
}
|
||||
const [offset, length] = entry
|
||||
const resourceOffset = resourceStart + offset
|
||||
const encoding = isString(options) ? options : null
|
||||
callback = typeof options === 'function' ? options : callback
|
||||
|
||||
fs.open(process.execPath, 'r', function (err, fd) {
|
||||
if (err) return callback(err, null)
|
||||
fs.read(fd, Buffer.alloc(length), 0, length, resourceOffset, function (error, bytesRead, buffer) {
|
||||
if (error) {
|
||||
return fs.close(fd, function () {
|
||||
callback(error, null)
|
||||
})
|
||||
}
|
||||
fs.close(fd, function (err) {
|
||||
if (err) {
|
||||
return callback(err, buffer)
|
||||
}
|
||||
const result = Buffer.from(buffer.toString(), 'base64')
|
||||
callback(err, encoding ? result.toString(encoding) : result)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fs.readFileSync = function readFileSync (file, options) {
|
||||
setupManifest()
|
||||
const manifest = process.__nexe
|
||||
const entry = manifest && manifest.resources[file]
|
||||
if (!manifest || !entry || !isString(file)) {
|
||||
return originalReadFileSync.apply(fs, arguments)
|
||||
}
|
||||
const [offset, length] = entry
|
||||
const resourceOffset = resourceStart + offset
|
||||
const encoding = isString(options) ? options : null
|
||||
const fd = fs.openSync(process.execPath, 'r')
|
||||
const result = Buffer.alloc(length)
|
||||
fs.readSync(fd, result, 0, length, resourceOffset)
|
||||
fs.closeSync(fd)
|
||||
const contents = Buffer.from(result.toString(), 'base64')
|
||||
return encoding ? contents.toString(encoding) : contents
|
||||
}
|
||||
}
|
||||
|
||||
const contentBuffer = Buffer.from(Array(contentSize));
|
||||
fs.readSync(fd, contentBuffer, 0, contentSize, contentStart);
|
||||
fs.closeSync(fd);
|
||||
const Module = require('module');
|
||||
process.mainModule = new Module(process.execPath, null);
|
||||
process.mainModule.loaded = true;
|
||||
process.mainModule._compile(contentBuffer.toString(), process.execPath);
|
||||
`.trim()
|
||||
'{{replace:lib/patches/boot-nexe.js}}'
|
||||
)
|
||||
return next()
|
||||
}
|
||||
|
||||
+7
-5
@@ -1,10 +1,11 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { FuseBox, JSONPlugin, CSSPlugin, HTMLPlugin, QuantumPlugin } from 'fuse-box'
|
||||
import { readFileAsync, writeFileAsync } from '../util'
|
||||
import { resolve, relative } from 'path'
|
||||
import NativeModulePlugin from '../bundling/fuse-native-module-plugin'
|
||||
import { NexeOptions } from '../options'
|
||||
|
||||
function createBundle(options: NexeOptions) {
|
||||
const { FuseBox, JSONPlugin, CSSPlugin, HTMLPlugin, QuantumPlugin } = require('fuse-box')
|
||||
const plugins: any = [JSONPlugin(), CSSPlugin(), HTMLPlugin(), NativeModulePlugin(options.native)]
|
||||
if (options.compress) {
|
||||
plugins.push(
|
||||
@@ -25,10 +26,11 @@ function createBundle(options: NexeOptions) {
|
||||
target: 'server',
|
||||
plugins
|
||||
})
|
||||
fuse.bundle(options.name).instructions(`> ${options.input}`)
|
||||
return fuse.run().then(x => {
|
||||
const input = relative(options.cwd, options.input).replace(/\\/g, '/')
|
||||
fuse.bundle(options.name).instructions(`> ${input}`)
|
||||
return fuse.run().then((x: any) => {
|
||||
let output = ''
|
||||
x.bundles.forEach(y => (output = y.context.output.lastPrimaryOutput.content!.toString()))
|
||||
x.bundles.forEach((y: any) => (output = y.context.output.lastPrimaryOutput.content.toString()))
|
||||
return output
|
||||
})
|
||||
}
|
||||
@@ -46,7 +48,7 @@ export default async function bundle(compiler: NexeCompiler, next: any) {
|
||||
|
||||
let producer = createBundle
|
||||
if (typeof compiler.options.bundle === 'string') {
|
||||
producer = require(compiler.options.bundle).createBundle
|
||||
producer = require(resolve(compiler.options.cwd, compiler.options.bundle)).createBundle
|
||||
}
|
||||
|
||||
compiler.input = await producer(compiler.options)
|
||||
|
||||
+22
-20
@@ -5,15 +5,18 @@ import { readFileAsync, dequote, isWindows } from '../util'
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { NexeTarget } from '../target'
|
||||
|
||||
function readStreamAsync(stream: NodeJS.ReadableStream): PromiseLike<string> {
|
||||
function getStdIn(stdin: NodeJS.ReadStream): Promise<string> {
|
||||
return new Promise(resolve => {
|
||||
let input = ''
|
||||
stream.setEncoding('utf-8')
|
||||
stream.on('data', (x: string) => {
|
||||
input += x
|
||||
})
|
||||
stream.once('end', () => resolve(dequote(input)))
|
||||
stream.resume && stream.resume()
|
||||
let out = ''
|
||||
stdin
|
||||
.setEncoding('utf8')
|
||||
.on('readable', () => {
|
||||
let current
|
||||
while ((current = stdin.read())) {
|
||||
out += current
|
||||
}
|
||||
})
|
||||
.on('end', () => resolve(out))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,20 +35,15 @@ function readStreamAsync(stream: NodeJS.ReadableStream): PromiseLike<string> {
|
||||
* @param {*} next
|
||||
*/
|
||||
export default async function cli(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
const { output } = compiler.options
|
||||
const { log, input: bundledInput } = compiler
|
||||
|
||||
if (!process.stdin.isTTY) {
|
||||
log.step('Using stdin as input')
|
||||
compiler.input = await readStreamAsync(process.stdin)
|
||||
const { log } = compiler
|
||||
let stdInUsed = false
|
||||
if (!process.stdin.isTTY && compiler.options.enableStdIn) {
|
||||
stdInUsed = true
|
||||
compiler.input = dequote(await getStdIn(process.stdin))
|
||||
}
|
||||
|
||||
await next()
|
||||
|
||||
compiler.output = isWindows
|
||||
? `${(output || compiler.options.name).replace(/\.exe$/, '')}.exe`
|
||||
: `${output || compiler.options.name}`
|
||||
|
||||
const target = compiler.options.targets.shift() as NexeTarget
|
||||
const deliverable = await compiler.compileAsync(target)
|
||||
|
||||
@@ -58,8 +56,12 @@ export default async function cli(compiler: NexeCompiler, next: () => Promise<vo
|
||||
if (e) {
|
||||
reject(e)
|
||||
} else if (compiler.output) {
|
||||
chmodSync(compiler.output, '755')
|
||||
step.log(`Executable written to: ${compiler.output}`)
|
||||
chmodSync(compiler.output, '755') //todo fix erroneous rw mode change
|
||||
step.log(
|
||||
`Entry: '${stdInUsed
|
||||
? compiler.options.empty ? '[empty]' : '[stdin]'
|
||||
: compiler.options.input}' written to: ${compiler.output}`
|
||||
)
|
||||
resolve(compiler.quit())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,15 +18,8 @@ export default async function resource(compiler: NexeCompiler, next: () => Promi
|
||||
count++
|
||||
step.log(`Including file: ${file}`)
|
||||
const contents = await readFileAsync(file)
|
||||
const commentSafeContents = contents.toString('base64')
|
||||
resources.index[file] = [
|
||||
Buffer.byteLength(resources.bundle),
|
||||
Buffer.byteLength(commentSafeContents)
|
||||
]
|
||||
resources.bundle += commentSafeContents
|
||||
compiler.addResource(file, contents)
|
||||
})
|
||||
step.log(
|
||||
`Included ${count} file(s). ${(Buffer.byteLength(resources.bundle) / 1e6).toFixed(3)} MB`
|
||||
)
|
||||
step.log(`Included ${count} file(s). ${(resources.bundle.byteLength / 1e6).toFixed(3)} MB`)
|
||||
return next()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { Stats } from 'fs'
|
||||
import { ok } from 'assert'
|
||||
import { resolve, normalize } from 'path'
|
||||
|
||||
const binary = (process as any).__nexe as NexeBinary
|
||||
ok(binary)
|
||||
const manifest = binary.resources
|
||||
const isString = (x: any): x is string => typeof x === 'string' || x instanceof String
|
||||
|
||||
if (Object.keys(manifest).length) {
|
||||
const fs = require('fs')
|
||||
const originalReadFile = fs.readFile
|
||||
const originalReadFileSync = fs.readFileSync
|
||||
const resourceStart = binary.layout.resourceStart
|
||||
|
||||
let setupManifest = () => {
|
||||
const manifest = binary.resources
|
||||
Object.keys(manifest).forEach(key => {
|
||||
const absolutePath = resolve(key)
|
||||
if (!manifest[absolutePath]) {
|
||||
manifest[absolutePath] = manifest[key]
|
||||
}
|
||||
const normalizedPath = normalize(key)
|
||||
if (!manifest[normalizedPath]) {
|
||||
manifest[normalizedPath] = manifest[key]
|
||||
}
|
||||
})
|
||||
setupManifest = () => {}
|
||||
}
|
||||
//TODO track inflight fs reqs??
|
||||
var nfs = {
|
||||
readFile: function readFile(file: any, options: any, callback: any) {
|
||||
setupManifest()
|
||||
const entry = manifest[file]
|
||||
if (!entry || !isString(file)) {
|
||||
return originalReadFile.apply(fs, arguments)
|
||||
}
|
||||
const [offset, length] = entry
|
||||
const resourceOffset = resourceStart + offset
|
||||
const encoding = isString(options) ? options : null
|
||||
callback = typeof options === 'function' ? options : callback
|
||||
|
||||
fs.open(process.execPath, 'r', function(err: Error, fd: number) {
|
||||
if (err) return callback(err, null)
|
||||
fs.read(fd, Buffer.alloc(length), 0, length, resourceOffset, function(
|
||||
error: Error,
|
||||
bytesRead: number,
|
||||
result: Buffer
|
||||
) {
|
||||
if (error) {
|
||||
return fs.close(fd, function() {
|
||||
callback(error, null)
|
||||
})
|
||||
}
|
||||
fs.close(fd, function(err: Error) {
|
||||
if (err) {
|
||||
return callback(err, result)
|
||||
}
|
||||
callback(err, encoding ? result.toString(encoding) : result)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
readFileSync: function readFileSync(file: any, options: any) {
|
||||
setupManifest()
|
||||
const entry = manifest[file]
|
||||
if (!entry || !isString(file)) {
|
||||
return originalReadFileSync.apply(fs, arguments)
|
||||
}
|
||||
const [offset, length] = entry
|
||||
const resourceOffset = resourceStart + offset
|
||||
const encoding = isString(options) ? options : null
|
||||
const fd = fs.openSync(process.execPath, 'r')
|
||||
const result = Buffer.alloc(length)
|
||||
fs.readSync(fd, result, 0, length, resourceOffset)
|
||||
fs.closeSync(fd)
|
||||
return encoding ? result.toString(encoding) : result
|
||||
}
|
||||
}
|
||||
Object.assign(fs, nfs)
|
||||
}
|
||||
|
||||
interface NexeBinary {
|
||||
resources: { [key: string]: number[] }
|
||||
version: string
|
||||
layout: {
|
||||
stat: Stats
|
||||
contentSize: number
|
||||
contentStart: number
|
||||
resourceSize: number
|
||||
resourceStart: number
|
||||
}
|
||||
}
|
||||
+17
-10
@@ -1,18 +1,25 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
|
||||
function wrap(code: string) {
|
||||
return '!(function () {' + code + '})();'
|
||||
}
|
||||
|
||||
export default function(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
if (!compiler.options.fakeArgv) {
|
||||
return next()
|
||||
compiler.shims.push(wrap(compiler.getHeader()))
|
||||
|
||||
if (compiler.options.resources.length) {
|
||||
compiler.shims.push(wrap('{{replace:lib/steps/shim-fs.js}}'))
|
||||
}
|
||||
|
||||
const nty = !process.stdin.isTTY
|
||||
const input = nty ? '[stdin]' : compiler.options.input
|
||||
|
||||
compiler.input =
|
||||
`!(() => {
|
||||
var r = require('path').resolve;
|
||||
process.argv.splice(1,0, ${nty ? `'${input}'` : `r("${input}")`});
|
||||
})();` + compiler.input
|
||||
if (compiler.options.fakeArgv) {
|
||||
const nty = !process.stdin.isTTY
|
||||
const input = nty ? '[stdin]' : compiler.options.input
|
||||
compiler.shims.push(
|
||||
wrap(`
|
||||
var r = require('path').resolve;
|
||||
process.argv.splice(1,0, ${nty ? `'${input}'` : `r("${input}")`});`)
|
||||
)
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
|
||||
+3
-1
@@ -28,6 +28,7 @@ const prettyPlatform: { [key: string]: NodePlatform } = {
|
||||
//TODO arm
|
||||
const prettyArch: { [key: string]: NodeArch } = {
|
||||
x86: 'x86',
|
||||
amd64: 'x64',
|
||||
ia32: 'x86',
|
||||
x32: 'x86',
|
||||
x64: 'x64'
|
||||
@@ -63,7 +64,8 @@ export function targetsEqual(a: NexeTarget, b: NexeTarget) {
|
||||
}
|
||||
|
||||
export function getTarget(target: string | Partial<NexeTarget> = ''): NexeTarget {
|
||||
let arch = process.arch as NodeArch,
|
||||
const currentArch = process.arch
|
||||
let arch = currentArch in prettyArch ? prettyArch[process.arch] : process.arch as NodeArch,
|
||||
platform = prettyPlatform[process.platform],
|
||||
version = process.version.slice(1)
|
||||
|
||||
|
||||
+17
@@ -26,6 +26,22 @@ function padRight(str: string, l: number) {
|
||||
return (str + ' '.repeat(l)).substr(0, l)
|
||||
}
|
||||
|
||||
const bound: MethodDecorator = function bound<T>(
|
||||
target: Object,
|
||||
propertyKey: string,
|
||||
descriptor: TypedPropertyDescriptor<T>
|
||||
) {
|
||||
const configurable = true
|
||||
return {
|
||||
configurable,
|
||||
get(this: T) {
|
||||
const value = (descriptor.value as any).bind(this)
|
||||
Object.defineProperty(this, propertyKey, { configurable, value, writable: true })
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dequote(input: string) {
|
||||
input = input.trim()
|
||||
const singleQuote = input.startsWith("'") && input.endsWith("'")
|
||||
@@ -62,6 +78,7 @@ function isDirectoryAsync(path: string) {
|
||||
export {
|
||||
dequote,
|
||||
padRight,
|
||||
bound,
|
||||
isWindows,
|
||||
rimrafAsync,
|
||||
statAsync,
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import * as nexe from '../src/nexe'
|
||||
import * as nexe from '../lib/nexe'
|
||||
import {
|
||||
getUnBuiltReleases,
|
||||
getLatestGitRelease
|
||||
} from '../src/releases'
|
||||
} from '../lib/releases'
|
||||
import * as ci from './ci'
|
||||
import { runAlpineBuild } from './docker'
|
||||
import { getTarget } from '../src/target'
|
||||
import { pathExistsAsync, statAsync, readFileAsync, execFileAsync } from '../src/util'
|
||||
import { getTarget } from '../lib/target'
|
||||
import { pathExistsAsync, statAsync, readFileAsync, execFileAsync } from '../lib/util'
|
||||
import got = require('got')
|
||||
|
||||
const env = process.env,
|
||||
branchName = env.CIRCLE_BRANCH || env.APPVEYOR_REPO_BRANCH || env.TRAVIS_BRANCH || '',
|
||||
isScheduled = Boolean(env.APPVEYOR_SCHEDULED_BUILD || env.NEXE_TRIGGERED),
|
||||
isLinux = Boolean(env.CIRCLECI),
|
||||
isLinux = Boolean(env.TRAVIS),
|
||||
isWindows = Boolean(env.APPVEYOR),
|
||||
isMac = Boolean(env.TRAVIS),
|
||||
isMac = Boolean(env.CIRCLECI),
|
||||
isPullRequest = Boolean(env.CIRCLE_PR_NUMBER)
|
||||
|| Boolean(env.APPVEYOR_PULL_REQUEST_NUMBER)
|
||||
|| Boolean(env.TRAVIS_PULL_REQUEST_BRANCH),
|
||||
@@ -66,13 +66,12 @@ async function build () {
|
||||
|
||||
const stop = keepalive()
|
||||
if (target.platform === 'alpine') {
|
||||
await runAlpineBuild(target, nexe.version.split('.')[0])
|
||||
await runAlpineBuild(target)
|
||||
} else {
|
||||
await nexe.compile(options)
|
||||
}
|
||||
stop()
|
||||
|
||||
|
||||
if (await pathExistsAsync(output)) {
|
||||
await assertNexeBinary(output)
|
||||
const gitRelease = await getLatestGitRelease({ headers })
|
||||
+19
-15
@@ -1,9 +1,23 @@
|
||||
import { NexeTarget } from '../src/releases'
|
||||
import { NexeTarget } from '../lib/releases'
|
||||
import got = require('got')
|
||||
import * as assert from 'assert'
|
||||
const { env } = process
|
||||
|
||||
export function triggerMacBuild(release: NexeTarget, branch: string) {
|
||||
assert.ok(env.CIRCLE_TOKEN)
|
||||
const circle = `https://circleci.com/api/v1.1/project/github/nexe/nexe/tree/${
|
||||
branch}?circle-token=${env.CIRCLE_TOKEN}`
|
||||
return got(circle, {
|
||||
json: true,
|
||||
body: {
|
||||
build_parameters: {
|
||||
NEXE_VERSION: release.toString()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function triggerDockerBuild (release: NexeTarget, branch: string) {
|
||||
assert.ok(env.TRAVIS_TOKEN)
|
||||
const travis = `https://api.travis-ci.org/repo/nexe%2Fnexe/requests`
|
||||
return got(travis, {
|
||||
@@ -14,7 +28,10 @@ export function triggerMacBuild(release: NexeTarget, branch: string) {
|
||||
config: {
|
||||
merge_mode: 'deep_merge',
|
||||
env: {
|
||||
NEXE_VERSION: release.toString()
|
||||
//use matrix so that secure global variable is merged
|
||||
matrix: {
|
||||
NEXE_VERSION: release.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,19 +43,6 @@ export function triggerMacBuild(release: NexeTarget, branch: string) {
|
||||
})
|
||||
}
|
||||
|
||||
export function triggerDockerBuild (release: NexeTarget, branch: string) {
|
||||
assert.ok(env.CIRCLE_TOKEN)
|
||||
const circle = `https://circleci.com/api/v1.1/project/github/nexe/nexe/tree/${branch}?circle-token=${env.CIRCLE_TOKEN}`
|
||||
return got(circle, {
|
||||
json: true,
|
||||
body: {
|
||||
build_parameters: {
|
||||
NEXE_VERSION: release.toString()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function triggerWindowsBuild (release: NexeTarget) {
|
||||
const hasVersion = 'NEXE_VERSION' in env
|
||||
env.NEXE_VERSION = hasVersion
|
||||
|
||||
+25
-44
@@ -1,73 +1,54 @@
|
||||
import { NexeTarget } from '../src/target'
|
||||
import { writeFileAsync, readFileAsync } from '../src/util'
|
||||
import { NexeTarget } from '../lib/target'
|
||||
import { writeFileAsync, readFileAsync } from '../lib/util'
|
||||
import { spawn } from 'child_process'
|
||||
import got = require('got')
|
||||
import { createWriteStream, WriteStream } from 'fs'
|
||||
import execa = require('execa')
|
||||
import { appendFileSync } from 'fs'
|
||||
|
||||
function alpine (target: NexeTarget, nexeVersion: string) {
|
||||
function alpine (target: NexeTarget) {
|
||||
return `
|
||||
FROM 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
|
||||
ENV NODE_VERSION=${target.version}
|
||||
ENV NEXE_VERSION=beta
|
||||
WORKDIR /
|
||||
|
||||
RUN curl -sSL https://nodejs.org/dist/v\${NODE_VERSION}/node-v\${NODE_VERSION}.tar.gz | tar -xz && \
|
||||
cd /node-v\${NODE_VERSION} && \
|
||||
mkdir /nexe_temp && mv /node-v\${NODE_VERSION} /nexe_temp/\${NODE_VERSION} && \
|
||||
cd /nexe_temp/\${NODE_VERSION} && \
|
||||
./configure --prefix=/usr --fully-static && \
|
||||
make -j$(getconf _NPROCESSORS_ONLN) && \
|
||||
make install && \
|
||||
make && make install && \
|
||||
paxctl -cm /usr/bin/node
|
||||
|
||||
RUN mkdir /nexe_temp && mv node-v\${NODE_VERSION} /nexe_temp/\${NODE_VERSION} && \
|
||||
RUN rm /nexe_temp/\${NODE_VERSION}/out/Release/node && \
|
||||
npm install -g nexe@\${NEXE_VERSION} && \
|
||||
nexe --build --empty --temp /nexe_temp -c="--fully-static" -o out
|
||||
`.trim()
|
||||
}
|
||||
|
||||
export async function runAsync (command: string, output: WriteStream) {
|
||||
const commands = command.split(/\r?\n/)
|
||||
.filter(x => x.trim())
|
||||
.map(x => x.trim().split(' '))
|
||||
const sequence = Promise.resolve()
|
||||
for (const command of commands) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const cp = spawn(command.shift() as string, command)
|
||||
cp.stderr.pipe(output)
|
||||
cp.stdout.pipe(output)
|
||||
const close = function (this: any, e: Error) {
|
||||
this.kill()
|
||||
e ? reject(e) : resolve()
|
||||
}
|
||||
cp.on('error', (e) => {
|
||||
output.write(e.stack)
|
||||
})
|
||||
.on('close', close)
|
||||
.on('exit', close)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function runAlpineBuild (target: NexeTarget, nexeVersion: string) {
|
||||
await writeFileAsync('Dockerfile', alpine(target, nexeVersion))
|
||||
export async function runAlpineBuild (target: NexeTarget) {
|
||||
await writeFileAsync('Dockerfile', alpine(target))
|
||||
const outFilename = 'nexe-alpine-build-log.txt'
|
||||
const output = createWriteStream(outFilename)
|
||||
await writeFileAsync(outFilename, '')
|
||||
let output: any = []
|
||||
|
||||
try {
|
||||
await runAsync(`
|
||||
docker build -t nexe-alpine .
|
||||
docker run -d --name nexe nexe-alpine sh
|
||||
docker cp nexe:/out out
|
||||
docker rm nexe
|
||||
`, output)
|
||||
output.push(await execa.shell(`docker build -t nexe-alpine .`))
|
||||
output.push(await execa.shell(`docker run -d --name nexe nexe-alpine sh`))
|
||||
output.push(await execa.shell(`docker cp nexe:/out out`))
|
||||
output.push(await execa.shell(`docker rm nexe`))
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
console.log('Error running docker', e)
|
||||
} finally {
|
||||
output.close()
|
||||
output.forEach((x: any) => {
|
||||
appendFileSync(outFilename, x.stderr)
|
||||
appendFileSync(outFilename, x.stdout)
|
||||
})
|
||||
await got(`https://transfer.sh/${Math.random().toString(36).substring(2)}.txt`, {
|
||||
body: await readFileAsync(outFilename),
|
||||
method: 'PUT'
|
||||
})
|
||||
.then(x => console.log(x.body))
|
||||
.then(x => console.log('Posted docker log: ', x.body))
|
||||
.catch(e => console.log('Error posting log', e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { writeFileSync, readFileSync } from 'fs'
|
||||
|
||||
/**
|
||||
* post build step to insert code files into code files.
|
||||
* '{{replace:path/to/file}}' => "file contents"
|
||||
* And the package.json version.
|
||||
*/
|
||||
//inject('plugins/nexe-daemon/lib/index.js')
|
||||
inject('lib/patches/third-party-main.js')
|
||||
inject('lib/steps/shim.js')
|
||||
inject('lib/options.js', JSON.stringify(require('../package.json').version))
|
||||
|
||||
function inject (filename: string, ...replacements: string[]) {
|
||||
let contents = readFileSync(filename, 'utf8')
|
||||
contents = contents.replace(/('{{(.*)}}')/g, (substring: string, ...matches: string[]) => {
|
||||
if (!matches || !matches[1]) {
|
||||
return substring
|
||||
}
|
||||
const [replace, file] = matches[1].split(':')
|
||||
if (replace !== 'replace') {
|
||||
return substring
|
||||
}
|
||||
console.log('Replacing: ', substring)
|
||||
return replacements[+file]
|
||||
? replacements[+file]
|
||||
: JSON.stringify(readFileSync(file, 'utf8'))
|
||||
})
|
||||
writeFileSync(filename, contents)
|
||||
console.log(`Wrote: ${filename}`)
|
||||
}
|
||||
@@ -10,7 +10,9 @@ describe('Targets', () => {
|
||||
['win-ia32-6.11.2', 'windows-x86-6.11.2'],
|
||||
[{ version: '6.11.2', platform: 'win', arch: 'ia32' }, 'windows-x86-6.11.2'],
|
||||
['win32-x64-6.11.2', 'windows-x64-6.11.2'],
|
||||
['win-amd64-6.11.2', 'windows-x64-6.11.2'],
|
||||
['darwin-x64-v8.4.0', 'mac-x64-8.4.0'],
|
||||
['macos-x64-v8.4.0', 'mac-x64-8.4.0'],
|
||||
['static-x86-6.10.3', 'alpine-x86-6.10.3'],
|
||||
['linux-x32', `linux-x86-${process.version.slice(1)}`],
|
||||
['alpine-notsupported-6.10.3', `alpine-${arch}-6.10.3`],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target":"es5",
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["es2017"],
|
||||
"module": "commonjs",
|
||||
"outDir": "./lib",
|
||||
|
||||
Reference in New Issue
Block a user