Compare commits

...

6 Commits

Author SHA1 Message Date
calebboyd 563aa97a89 fix: add bundle directly to third_party_main 2017-08-13 15:28:32 -05:00
calebboyd 88a46dc730 Merge branch 'cb-wip' 2017-08-03 23:00:51 -05:00
calebboyd 1a6b113d6e chore: wip bundling 2017-08-03 22:59:15 -05:00
calebboyd d375c8f214 chore: indicate beta in readme 2017-07-20 16:20:40 -05:00
calebboyd 15d919c302 chore: docs and ts fixes 2017-07-18 11:08:45 -05:00
calebboyd 6adf3c82c3 chore: update fusebox example version 2017-07-17 09:38:40 -05:00
15 changed files with 2958 additions and 3083 deletions
+4 -1
View File
@@ -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"]
}
+30 -30
View File
@@ -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.
+4 -2
View File
@@ -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"
}
}
+2822
View File
File diff suppressed because it is too large Load Diff
+7 -5
View File
@@ -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
View File
@@ -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) {
+31
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+17
View File
@@ -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()
}
+2
View File
@@ -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,
+1 -4
View File
@@ -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()
}
-3004
View File
File diff suppressed because it is too large Load Diff