Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 563aa97a89 | |||
| 88a46dc730 | |||
| 1a6b113d6e | |||
| d375c8f214 | |||
| 15d919c302 | |||
| 6adf3c82c3 |
Vendored
+4
-1
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"typescript.tsdk": "./node_modules/typescript/lib"
|
||||
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||
"prettier.semi": false,
|
||||
"prettier.singleQuote": true,
|
||||
"prettier.typescriptEnable": ["typescript"]
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<a href="https://www.npmjs.com/package/nexe"><img src="https://img.shields.io/npm/l/nexe.svg" alt="License"></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><code>npm i nexe -g</code></p>
|
||||
<p align="center"><code>npm i nexe@beta -g</code></p>
|
||||
<p align="center">Nexe is a command-line utility that compiles your Node.js application into a single executable file.</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -25,7 +25,7 @@
|
||||
- Flexible build pipeline
|
||||
- Cross platform builds
|
||||
|
||||
## Usage
|
||||
## Usage (Beta)
|
||||
|
||||
*Note: V2 API is still subject to change. * For v1 see [V1-EOL](https://github.com/nexe/nexe/tree/V1-EOL)
|
||||
|
||||
@@ -33,9 +33,9 @@
|
||||
|
||||
`nexe -i ./my-app-bundle.js -o ./my-app.exe`
|
||||
|
||||
- stdin & stdout interfaces
|
||||
- stdin interface
|
||||
|
||||
`rollup -c | nexe --resource ./public/**/* > my-app.exe`
|
||||
`rollup -c | nexe --resource ./public/**/* -o my-app.exe`
|
||||
|
||||
For more CLI options see: `nexe --help`
|
||||
|
||||
@@ -78,73 +78,77 @@ nexe.compile({
|
||||
|
||||
### `options`
|
||||
|
||||
- `build: boolean`
|
||||
- #### `build: boolean`
|
||||
- Build node from source (required in beta)
|
||||
- `input: string`
|
||||
- #### `input: string`
|
||||
- Input bundle file path
|
||||
- default: stdin or the current directory's main file (package.json)
|
||||
- `output: string`
|
||||
- #### `output: string`
|
||||
- Output executable file path
|
||||
- default: same as `name` with an OS specific extension.
|
||||
- `target: string`
|
||||
- #### `target: string`
|
||||
- Dash seperated platform-architecture-version. e.g. `'win32-ia32-6.10.3'`
|
||||
- default: `[process.platform, process.arch, process.version.slice(1)].join('-')`
|
||||
- `name: string`
|
||||
- #### `bundle: string`
|
||||
- Path to a custom bundling function. When executed it should either return a fusebox producer, or a string.
|
||||
- If it is a string it will be required and the export `nexeBundle` will be used
|
||||
- Defaults to the internal fuse-box configuration
|
||||
- #### `name: string`
|
||||
- Module friendly name of the application
|
||||
- default: basename of the input file, or `nexe_${Date.now()}`
|
||||
- `version: string`
|
||||
- #### `version: string`
|
||||
- The Node version you're building for
|
||||
- default: `process.version.slice(1)`
|
||||
- `python: string`
|
||||
- #### `python: string`
|
||||
- On Linux this is the path pointing to your python2 executable
|
||||
- On Windows this is the directory where `python` can be accessed
|
||||
- default: `null`
|
||||
- `flags: Array<string>`
|
||||
- #### `flags: Array<string>`
|
||||
- Array of node runtime flags to build node with.
|
||||
- Example: `['--expose-gc']`
|
||||
- default: `[]`
|
||||
- `configure: Array<string>`
|
||||
- #### `configure: Array<string>`
|
||||
- Array of arguments for the node build configure step
|
||||
- Example: `['--with-dtrace', '--dest-cpu=x64']`
|
||||
- default: `[]`
|
||||
- `make: Array<string>`
|
||||
- #### `make: Array<string>`
|
||||
- Array of arguments for the node build make step
|
||||
- default: `[]`
|
||||
- `vcBuild: Array<string>`
|
||||
- #### `vcBuild: Array<string>`
|
||||
- Array of arguments for the node build step on windows
|
||||
- default: `['nosign', 'release']`
|
||||
- `snapshot: string`
|
||||
- #### `snapshot: string`
|
||||
- path to a file to be used as the warmup snapshot for the build
|
||||
- default: `null`
|
||||
- `resources: Array<string>`
|
||||
- #### `resources: Array<string>`
|
||||
- Array of globs with files to include in the build
|
||||
- Example: `['./public/**/*']`
|
||||
- default: `[]`
|
||||
- `temp: string`
|
||||
- #### `temp: string`
|
||||
- Path to use for storing nexe's build files
|
||||
- Override in the env with `NEXE_TEMP`
|
||||
- default: `./.nexe` in the cwd
|
||||
- `ico: string`
|
||||
- #### `ico: string`
|
||||
- Path to a user provided icon to be used (Windows only).
|
||||
- `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).
|
||||
- Example: `{ CompanyName: "ACME Corp" }`
|
||||
- default: `{}`
|
||||
- `clean: boolean`
|
||||
- #### `clean: boolean`
|
||||
- If included, nexe will remove temporary files for accompanying configuration and exit
|
||||
- `enableNodeCli: boolean`
|
||||
- #### `enableNodeCli: boolean`
|
||||
- Enable the original Node CLI (will prevent application cli from working)
|
||||
- default: `false`
|
||||
- `sourceUrl: string`
|
||||
- #### `sourceUrl: string`
|
||||
- Provide an alternate url for the node source. Should be a `.tar.gz`
|
||||
- `loglevel: string`
|
||||
- #### `loglevel: string`
|
||||
- Set the loglevel, info, silent, or verbose
|
||||
- default: `'info'`
|
||||
- `padding`
|
||||
- #### `padding`
|
||||
- Advanced option for controlling the size available in the executable.
|
||||
- It must be larger than the bundle + resources in bytes
|
||||
- default: 3, 6, 9, 16, 25, or 40 MB sizes are selected automatically.
|
||||
- `patches: Array<NexePatch>`
|
||||
- #### `patches: Array<NexePatch>`
|
||||
- Userland patches for patching or modifying node source
|
||||
- default: `[]`
|
||||
|
||||
@@ -171,10 +175,6 @@ For examples, see the built in patches: [src/patches](src/patches)
|
||||
|
||||
Any modifications made to `SourceFile#contents` will be maintained in the cache _without_ the need to explicitly write them back out, e.g. using `NexeCompiler#setFileContentsAsync`.
|
||||
|
||||
## Bundling
|
||||
|
||||
Bundling in nexe has been decoupled from the compiler pipeline. While in beta it is completely seperate. Any bundle will always work with nexe as it is node compatible.
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"bundle": "node build",
|
||||
"build": "nexe -i app.js -o app.exe"
|
||||
"build-windows": "nexe -i app.js -o app.exe"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fuse-box": "^2.2.0",
|
||||
"zmq": "^2.15.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"fuse-box": "^2.2.1-beta.7"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+2822
File diff suppressed because it is too large
Load Diff
+7
-5
@@ -2,17 +2,17 @@
|
||||
"name": "nexe",
|
||||
"description": "Create a single executable out of your Node.js application",
|
||||
"license": "MIT",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.0-beta.4",
|
||||
"contributors": [
|
||||
"Craig Condon <craig.j.condon@gmail.com> (http://crcn.io)",
|
||||
"Jared Allard <jaredallard@outlook.com>",
|
||||
"Caleb Boyd <caleb.boyd@hotmail.com>"
|
||||
],
|
||||
"scripts": {
|
||||
"prebuild": "npm run clean",
|
||||
"prebuild": "rimraf lib && npm run lint",
|
||||
"prepublish": "npm run build",
|
||||
"clean": "rimraf lib",
|
||||
"build": "prettier --parser typescript --no-semi --print-width 100 --single-quote --write \"src/**/*.ts\" && tsc --declaration"
|
||||
"lint": "prettier --parser typescript --no-semi --print-width 100 --single-quote --write \"src/**/*.ts\"",
|
||||
"build": "tsc --declaration"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -33,6 +33,7 @@
|
||||
"bluebird": "^3.5.0",
|
||||
"chalk": "^1.1.3",
|
||||
"download": "^6.2.0",
|
||||
"fuse-box": "^2.2.1",
|
||||
"globby": "^6.1.0",
|
||||
"memory-fs": "^0.4.1",
|
||||
"minimist": "^1.2.0",
|
||||
@@ -45,12 +46,13 @@
|
||||
"devDependencies": {
|
||||
"@types/bluebird": "^3.5.8",
|
||||
"@types/chalk": "^0.4.31",
|
||||
"@types/globby": "^0.6.0",
|
||||
"@types/minimist": "^1.2.0",
|
||||
"@types/mkdirp": "^0.3.29",
|
||||
"@types/ora": "^0.3.31",
|
||||
"@types/rimraf": "0.0.28",
|
||||
"mocha": "^3.2.0",
|
||||
"prettier": "^1.4.4",
|
||||
"standard": "^10.0.1",
|
||||
"typescript": "^2.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
+11
-6
@@ -9,6 +9,7 @@ function hashName(name: string | Buffer) {
|
||||
|
||||
export interface FuseBoxFile {
|
||||
info: { absPath: string }
|
||||
absPath: string
|
||||
contents: string
|
||||
analysis: {
|
||||
dependencies: string[]
|
||||
@@ -34,6 +35,10 @@ export interface NativeModulePluginOptions {
|
||||
| true
|
||||
}
|
||||
|
||||
export default function(options: NativeModulePluginOptions) {
|
||||
return new NativeModulePlugin(options)
|
||||
}
|
||||
|
||||
export class NativeModulePlugin {
|
||||
public test: RegExp
|
||||
public limit2Project = false
|
||||
@@ -51,12 +56,12 @@ export class NativeModulePlugin {
|
||||
transform(file: FuseBoxFile) {
|
||||
file.loadContents()
|
||||
|
||||
if (file.info.absPath.endsWith('.node')) {
|
||||
const contents = readFileSync(file.info.absPath)
|
||||
if (file.absPath.endsWith('.node')) {
|
||||
const contents = readFileSync(file.absPath)
|
||||
const module = this.modules.find(x =>
|
||||
Boolean(~file.info.absPath.indexOf(join('node_modules', x)))
|
||||
Boolean(~file.absPath.indexOf(join('node_modules', x)))
|
||||
)!
|
||||
const bindingName = basename(file.info.absPath)
|
||||
const bindingName = basename(file.absPath)
|
||||
const settings = this.options[module]
|
||||
const moduleDir = hashName(contents)
|
||||
file.contents = `
|
||||
@@ -93,7 +98,7 @@ export class NativeModulePlugin {
|
||||
mkdirp(path.dirname(bindingFile));
|
||||
fs.writeFileSync(bindingFile, Buffer.from(binding, 'base64'));
|
||||
${settings.additionalFiles.reduce((code, filename, i) => {
|
||||
const contents = readFileSync(join(dirname(file.info.absPath), filename))
|
||||
const contents = readFileSync(join(dirname(file.absPath), filename))
|
||||
return (code += `
|
||||
var file${i} = '${contents.toString('base64')}';
|
||||
var filePath${i} = path.join(cwd, bindingFileParts[0], '${filename
|
||||
@@ -113,7 +118,7 @@ export class NativeModulePlugin {
|
||||
plugins: [
|
||||
{
|
||||
onNode(file, node, parent) {
|
||||
bindingsRewrite.onNode(file.info.absPath, node, parent)
|
||||
bindingsRewrite.onNode(file.absPath, node, parent)
|
||||
},
|
||||
onEnd(file) {
|
||||
if (bindingsRewrite.rewrite) {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
import { FuseBox, JSONPlugin, CSSPlugin, HTMLPlugin } from 'fuse-box'
|
||||
import NativeModulePlugin from './fuse'
|
||||
|
||||
function bundleProducer(filename: string) {
|
||||
const fuse = FuseBox.init({
|
||||
cache: false,
|
||||
log: Boolean(process.env.NEXE_BUNDLE_DEBUG) || false,
|
||||
homeDir: './',
|
||||
writeBundles: false,
|
||||
target: 'server',
|
||||
plugins: [JSONPlugin(), CSSPlugin(), HTMLPlugin()]
|
||||
})
|
||||
fuse.bundle('nexe').instructions(`> ${filename}`)
|
||||
return fuse.run().then(x => {
|
||||
if (x.bundles.size > 1) {
|
||||
console.log('THROW', x.bundles)
|
||||
}
|
||||
let output = ''
|
||||
x.bundles.forEach(y => (output = y.context.output.lastPrimaryOutput.content!.toString()))
|
||||
})
|
||||
}
|
||||
|
||||
export default function bundle(compiler: NexeCompiler, next: any) {
|
||||
if (!compiler.options.bundle) {
|
||||
return next()
|
||||
}
|
||||
|
||||
if (typeof compiler.options.bundle === 'string') {
|
||||
}
|
||||
}
|
||||
+16
-26
@@ -5,7 +5,7 @@ import { createWriteStream, chmodSync } from 'fs'
|
||||
import { readFileAsync, dequote, isWindows } from './util'
|
||||
import { NexeCompiler } from './compiler'
|
||||
|
||||
function readStreamAsync(stream: NodeJS.ReadStream): PromiseLike<string> {
|
||||
function readStreamAsync(stream: NodeJS.ReadableStream): PromiseLike<string> {
|
||||
return new Bluebird(resolve => {
|
||||
let input = ''
|
||||
stream.setEncoding('utf-8')
|
||||
@@ -49,35 +49,25 @@ export default async function cli(compiler: NexeCompiler, next: () => Promise<vo
|
||||
compiler.input = ''
|
||||
}
|
||||
|
||||
compiler.input = compiler.input.trim()
|
||||
await next()
|
||||
|
||||
const shouldPipeOutput = Boolean(!output && !process.stdout.isTTY)
|
||||
const outputName = output || `${compiler.options.name}${isWindows ? '.exe' : ''}`
|
||||
|
||||
compiler.output = shouldPipeOutput ? null : outputName
|
||||
compiler.output = output || `${compiler.options.name}${isWindows ? '.exe' : ''}`
|
||||
const deliverable = await compiler.compileAsync()
|
||||
|
||||
return new Bluebird((resolve, reject) => {
|
||||
deliverable.once('error', reject)
|
||||
|
||||
if (shouldPipeOutput) {
|
||||
log.step('Writing binary to stdout')
|
||||
deliverable.pipe(process.stdout).once('error', reject)
|
||||
resolve()
|
||||
} else if (compiler.output) {
|
||||
const step = log.step('Writing result to file')
|
||||
deliverable
|
||||
.pipe(createWriteStream(normalize(compiler.output)))
|
||||
.on('error', reject)
|
||||
.once('close', (e: Error) => {
|
||||
if (e) {
|
||||
reject(e)
|
||||
} else if (compiler.output) {
|
||||
chmodSync(compiler.output, '755')
|
||||
step.log(`Executable written to: ${compiler.output}`)
|
||||
resolve(compiler.quit())
|
||||
}
|
||||
})
|
||||
}
|
||||
const step = log.step('Writing result to file')
|
||||
deliverable
|
||||
.pipe(createWriteStream(normalize(compiler.output!)))
|
||||
.on('error', reject)
|
||||
.once('close', (e: Error) => {
|
||||
if (e) {
|
||||
reject(e)
|
||||
} else if (compiler.output) {
|
||||
chmodSync(compiler.output, '755')
|
||||
step.log(`Executable written to: ${compiler.output}`)
|
||||
resolve(compiler.quit())
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
+2
-1
@@ -229,7 +229,8 @@ export class NexeCompiler {
|
||||
await next()
|
||||
const header = compiler._generateHeader()
|
||||
const contents = inflate(this._getPayload(header), +header.paddingSize) + tail
|
||||
return compiler.setFileContentsAsync(`lib/${compiler.options.name}.js`, contents)
|
||||
const file = await compiler.readFileAsync('lib/_third_party_main.js')
|
||||
file.contents += '\n' + contents
|
||||
}
|
||||
|
||||
async compileAsync() {
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ function fetchNodeSourceAsync(cwd: string, url: string, step: LogStep, options =
|
||||
const setText = (p: number) => step.modify(`Downloading Node: ${p.toFixed()}%...`)
|
||||
return download(url, cwd, Object.assign(options, { extract: true, strip: 1 }))
|
||||
.on('response', (res: IncomingMessage) => {
|
||||
const total = +res.headers['content-length']
|
||||
const total = +res.headers['content-length']!
|
||||
let current = 0
|
||||
res.on('data', data => {
|
||||
current += data.length
|
||||
|
||||
+10
-3
@@ -56,6 +56,7 @@ export interface NexeOptions {
|
||||
clean: boolean
|
||||
enableNodeCli: boolean
|
||||
sourceUrl?: string
|
||||
bundle: boolean | string
|
||||
loglevel: 'info' | 'silent' | 'verbose'
|
||||
silent?: boolean
|
||||
verbose?: boolean
|
||||
@@ -80,6 +81,8 @@ const defaults = {
|
||||
targets: [],
|
||||
vcBuild: ['nosign', 'release'],
|
||||
enableNodeCli: false,
|
||||
bundle: true,
|
||||
build: true,
|
||||
patches: []
|
||||
}
|
||||
const alias = {
|
||||
@@ -104,6 +107,7 @@ const argv = parseArgv(process.argv, { alias, default: defaults })
|
||||
const help = `
|
||||
nexe --help CLI OPTIONS
|
||||
|
||||
-b --build -- build from source
|
||||
-i --input =index.js -- application entry point
|
||||
-o --output =my-app.exe -- path to output file
|
||||
-t --target =win32-x64-6.10.3 -- *target a prebuilt binary
|
||||
@@ -116,7 +120,9 @@ nexe --help CLI OPTIONS
|
||||
-vc --vcBuild =x64 -- *pass arguments to vcbuild.bat
|
||||
-s --snapshot =/path/to/snapshot -- build with warmup snapshot
|
||||
-r --resource =./paths/**/* -- *embed file bytes within the binary
|
||||
--bundle =./path/to/config -- pass a module path that exports nexeBundle
|
||||
--temp =./path/to/temp -- nexe temp files (for downloads and source builds)
|
||||
--no-bundle -- set when input is already bundled
|
||||
--ico -- file name for alternate icon file (windows)
|
||||
--rc-* -- populate rc file options (windows)
|
||||
--clean -- force download of sources
|
||||
@@ -175,8 +181,8 @@ function extractName(options: NexeOptions) {
|
||||
}
|
||||
|
||||
function normalizeOptionsAsync(input: Partial<NexeOptions>) {
|
||||
if (argv.help || argv._.some(x => x === 'version')) {
|
||||
process.stderr.write(argv.help ? help : '2.0.0-beta.1' + EOL, () => process.exit(0))
|
||||
if (argv.help || argv._.some((x: string) => x === 'version')) {
|
||||
process.stderr.write(argv.help ? help : '2.0.0-beta.4' + EOL, () => process.exit(0))
|
||||
}
|
||||
|
||||
const options = Object.assign({}, defaults, input) as NexeOptions
|
||||
@@ -188,9 +194,10 @@ function normalizeOptionsAsync(input: Partial<NexeOptions>) {
|
||||
options.targets = flattenFilter(opts.target, options.targets)
|
||||
options.make = flattenFilter(options.make)
|
||||
options.vcBuild = flattenFilter(options.vcBuild)
|
||||
options.configure = flattenFilter(options.configure)
|
||||
options.resources = flattenFilter(opts.resource, options.resources)
|
||||
options.rc = options.rc || extractCliMap(/^rc-.*/, options)
|
||||
options.build = true // FIXME
|
||||
|
||||
if (options.build || options.padding === 0) {
|
||||
options.targets = []
|
||||
options.build = true
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { NexeCompiler } from '../compiler'
|
||||
|
||||
export default async function buildFixes(compiler: NexeCompiler, next: () => Promise<void>) {
|
||||
if (!compiler.options.version.startsWith('8.2')) {
|
||||
return next()
|
||||
}
|
||||
|
||||
const file = await compiler.readFileAsync('./tools/msvs/find_python.cmd')
|
||||
|
||||
await compiler.replaceInFileAsync(
|
||||
'./tools/msvs/find_python.cmd',
|
||||
'%p%python.exe -V 2>&1',
|
||||
'"%p%python.exe" -V 2>&1'
|
||||
)
|
||||
|
||||
return next()
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import gyp from './gyp'
|
||||
import nexePatches from './third-party-main'
|
||||
import buildFixes from './build-fixes'
|
||||
import cli from './disable-node-cli'
|
||||
import flags from './flags'
|
||||
import ico from './ico'
|
||||
@@ -10,6 +11,7 @@ const patches = [
|
||||
gyp,
|
||||
(compiler: NexeCompiler, next: () => Promise<void>) => compiler.setMainModule(compiler, next),
|
||||
nexePatches,
|
||||
buildFixes,
|
||||
cli,
|
||||
flags,
|
||||
ico,
|
||||
|
||||
@@ -74,10 +74,7 @@ fs.readFileSync = function readFileSync (file, options) {
|
||||
fs.closeSync(fd)
|
||||
const contents = Buffer.from(result.toString(), 'base64')
|
||||
return encoding ? contents.toString(encoding) : contents
|
||||
}
|
||||
|
||||
require("${compiler.options.name}");
|
||||
`.trim()
|
||||
}`.trim()
|
||||
)
|
||||
return next()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user