diff --git a/.gitignore b/.gitignore
index 580e70a..57b1cc7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ coverage
node_modules
*.log
*.exe
+!winsw.exe
.idea
.nexe
lib
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..9cf9495
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+package-lock=false
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index fa95257..c076e13 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "nexe",
- "version": "2.0.0-rc.5",
+ "version": "2.0.0-rc.6",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -146,12 +146,6 @@
"json-stable-stringify": "1.0.1"
}
},
- "amdefine": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
- "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
- "optional": true
- },
"ansi": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz",
@@ -532,9 +526,9 @@
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
},
"content-type": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
- "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0="
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.3.1",
@@ -770,7 +764,7 @@
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"requires": {
- "iconv-lite": "0.4.18"
+ "iconv-lite": "0.4.19"
}
},
"end-of-stream": {
@@ -792,37 +786,26 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"escodegen": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz",
- "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.0.tgz",
+ "integrity": "sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw==",
"requires": {
- "esprima": "2.7.3",
- "estraverse": "1.9.3",
+ "esprima": "3.1.3",
+ "estraverse": "4.2.0",
"esutils": "2.0.2",
"optionator": "0.8.2",
- "source-map": "0.2.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
- "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=",
- "optional": true,
- "requires": {
- "amdefine": "1.0.1"
- }
- }
+ "source-map": "0.5.7"
}
},
"esprima": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
- "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE="
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
+ "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
},
"estraverse": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz",
- "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q="
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
},
"esutils": {
"version": "2.0.2",
@@ -830,9 +813,9 @@
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
},
"etag": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz",
- "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE="
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"exec-sh": {
"version": "0.2.1",
@@ -866,20 +849,20 @@
"accepts": "1.3.4",
"array-flatten": "1.1.1",
"content-disposition": "0.5.2",
- "content-type": "1.0.2",
+ "content-type": "1.0.4",
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "2.6.8",
"depd": "1.1.1",
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
- "etag": "1.8.0",
+ "etag": "1.8.1",
"finalhandler": "1.0.4",
"fresh": "0.5.0",
"merge-descriptors": "1.0.1",
"methods": "1.1.2",
"on-finished": "2.3.0",
- "parseurl": "1.3.1",
+ "parseurl": "1.3.2",
"path-to-regexp": "0.1.7",
"proxy-addr": "1.1.5",
"qs": "6.5.0",
@@ -920,7 +903,7 @@
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.4.tgz",
"integrity": "sha1-HtkZnanL/i7y96MbL96LDRI2iXI=",
"requires": {
- "iconv-lite": "0.4.18",
+ "iconv-lite": "0.4.19",
"jschardet": "1.5.1",
"tmp": "0.0.31"
}
@@ -1022,7 +1005,7 @@
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
"on-finished": "2.3.0",
- "parseurl": "1.3.1",
+ "parseurl": "1.3.2",
"statuses": "1.3.1",
"unpipe": "1.0.0"
}
@@ -1064,9 +1047,9 @@
}
},
"forwarded": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz",
- "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M="
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.1.tgz",
+ "integrity": "sha1-ik4wxkCwU5U5mjVJxzAldygEiWE="
},
"fresh": {
"version": "0.5.0",
@@ -1102,7 +1085,7 @@
"base64-js": "1.2.1",
"chokidar": "1.7.0",
"concat-with-sourcemaps": "1.0.4",
- "escodegen": "1.8.1",
+ "escodegen": "1.9.0",
"express": "4.15.4",
"fliplog": "0.3.13",
"fs-extra": "2.1.2",
@@ -1333,9 +1316,9 @@
}
},
"iconv-lite": {
- "version": "0.4.18",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz",
- "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA=="
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
+ "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
},
"ieee754": {
"version": "1.1.8",
@@ -2139,9 +2122,9 @@
}
},
"parseurl": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
- "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY="
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
},
"path-is-absolute": {
"version": "1.0.1",
@@ -2280,7 +2263,7 @@
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz",
"integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=",
"requires": {
- "forwarded": "0.1.0",
+ "forwarded": "0.1.1",
"ipaddr.js": "1.4.0"
}
},
@@ -2507,7 +2490,7 @@
"destroy": "1.0.4",
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
- "etag": "1.8.0",
+ "etag": "1.8.1",
"fresh": "0.5.0",
"http-errors": "1.6.2",
"mime": "1.3.4",
@@ -2524,7 +2507,7 @@
"requires": {
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
- "parseurl": "1.3.1",
+ "parseurl": "1.3.2",
"send": "0.15.4"
}
},
diff --git a/plugins/nexe-daemon/.npmrc b/plugins/nexe-daemon/.npmrc
new file mode 100644
index 0000000..9cf9495
--- /dev/null
+++ b/plugins/nexe-daemon/.npmrc
@@ -0,0 +1 @@
+package-lock=false
\ No newline at end of file
diff --git a/plugins/nexe-daemon/index.ts b/plugins/nexe-daemon/index.ts
new file mode 100644
index 0000000..eea8fd5
--- /dev/null
+++ b/plugins/nexe-daemon/index.ts
@@ -0,0 +1,5 @@
+import { NexeCompiler } from 'nexe'
+
+export default function daemon (compiler: NexeCompiler ) {
+ compiler.addResource('')
+}
diff --git a/plugins/nexe-daemon/package.json b/plugins/nexe-daemon/package.json
new file mode 100644
index 0000000..023e387
--- /dev/null
+++ b/plugins/nexe-daemon/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "nexe-daemon",
+ "version": "1.0.0",
+ "description": "Plugin for nexe to create installable services",
+ "main": "index.js",
+ "scripts": {},
+ "author": "Caleb Boyd",
+ "license": "MIT",
+ "devDependencies": {
+ "nexe": "^2.0.0-rc.6"
+ }
+}
diff --git a/plugins/nexe-daemon/winsw-config.xml.tmpl b/plugins/nexe-daemon/winsw-config.xml.tmpl
new file mode 100644
index 0000000..fd1ce1c
--- /dev/null
+++ b/plugins/nexe-daemon/winsw-config.xml.tmpl
@@ -0,0 +1,6 @@
+
+ {{id}}
+ {{name}}
+ {{description}}
+ %BASE%\{{executable}}.exe
+
\ No newline at end of file
diff --git a/plugins/nexe-daemon/winsw.exe b/plugins/nexe-daemon/winsw.exe
new file mode 100644
index 0000000..3f1f724
Binary files /dev/null and b/plugins/nexe-daemon/winsw.exe differ
diff --git a/src/compiler.ts b/src/compiler.ts
index fae5cb0..8a2747b 100644
--- a/src/compiler.ts
+++ b/src/compiler.ts
@@ -5,7 +5,7 @@ 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 { readFileAsync, writeFileAsync, pathExistsAsync, dequote, isWindows, bound } from './util'
import { NexeOptions, nexeVersion } from './options'
import { NexeTarget } from './target'
import download = require('download')
@@ -34,19 +34,16 @@ export class NexeCompiler {
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
- public writeFileAsync: (file: string, contents: Buffer | string) => Promise
- public replaceInFileAsync: (file: string, replacer: any, replaceValue: string) => Promise
- public setFileContentsAsync: (file: string, contents: string | Buffer) => Promise
private nodeSrcBinPath: string
constructor(public options: NexeOptions) {
@@ -65,32 +62,50 @@ 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() {
@@ -99,6 +114,12 @@ export class NexeCompiler {
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())
@@ -217,12 +238,13 @@ export class NexeCompiler {
artifact.push(chunk)
})
binary.on('close', () => {
- const content = this._serializeHeader(header) + this.input
+ const content = this._serializeHeader(header) + this.shims.join(';') + ';' + this.input
+
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(''), lengths]))
artifact.push(null)
})
diff --git a/src/options.ts b/src/options.ts
index f9ed512..af64223 100644
--- a/src/options.ts
+++ b/src/options.ts
@@ -148,10 +148,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 +158,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$/, '')
}
diff --git a/src/steps/resource.ts b/src/steps/resource.ts
index 5cdb88a..1822f84 100644
--- a/src/steps/resource.ts
+++ b/src/steps/resource.ts
@@ -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()
}
diff --git a/src/util.ts b/src/util.ts
index 8f2c57d..00182a8 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -26,6 +26,22 @@ function padRight(str: string, l: number) {
return (str + ' '.repeat(l)).substr(0, l)
}
+const bound: MethodDecorator = function bound(
+ target: Object,
+ propertyKey: string,
+ descriptor: TypedPropertyDescriptor
+) {
+ 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,
diff --git a/tsconfig.json b/tsconfig.json
index 57c70f2..d0bf374 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"target":"es5",
+ "experimentalDecorators": true,
"lib": ["es2017"],
"module": "commonjs",
"outDir": "./lib",