refactor: move startup scripts to shims, add plugins

This commit is contained in:
calebboyd
2017-09-26 20:16:03 -05:00
parent b822984776
commit 11fa087e9d
26 changed files with 2573 additions and 3205 deletions
+1
View File
@@ -4,6 +4,7 @@ coverage
node_modules
*.log
*.exe
*.bin
!winsw.exe
.idea
.nexe
-1
View File
@@ -1 +0,0 @@
package-lock=false
+9 -9
View File
@@ -1,9 +1,9 @@
os: osx
language: node_js
node_js:
- '6'
script: npm run nexe-build
env:
- secure: LyC6djYqPfPKz5gHILES9JmSRxQ+ntKOcoaZNM5WID0+gogUoXZBhcmmtvq9RnMpFZN3pYujY6LB5iy7QS2seGjg+feyewNKI30yIK6N9ulJyZZKmrJVDdRx4wvl8YVTkttjcJFkanDGa6zRvFfFDKx/iIdcWLEpnqR/WO1ppf8=
notifications:
email: false
os: osx
language: node_js
node_js:
- '6'
script: npm run asset-compile
env:
- secure: LyC6djYqPfPKz5gHILES9JmSRxQ+ntKOcoaZNM5WID0+gogUoXZBhcmmtvq9RnMpFZN3pYujY6LB5iy7QS2seGjg+feyewNKI30yIK6N9ulJyZZKmrJVDdRx4wvl8YVTkttjcJFkanDGa6zRvFfFDKx/iIdcWLEpnqR/WO1ppf8=
notifications:
email: false
+9 -3
View File
@@ -51,7 +51,7 @@ Additional files or resources can be added to the binary by passing `-r "glob/pa
## 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). 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)
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
@@ -104,7 +104,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
@@ -163,12 +163,16 @@ 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)
A plugin
### `NexeCompiler`
@@ -178,6 +182,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.
+1 -1
View File
@@ -14,7 +14,7 @@ install:
build: off
test: off
build_script:
- npm run nexe-build
- npm run asset-compile
artifacts:
- path: .nexe\**\node.exe
cache:
+1 -1
View File
@@ -5,4 +5,4 @@ machine:
- docker
test:
override:
- npm run nexe-build
- npm run asset-compile
+16 -8
View File
@@ -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')
}
-2983
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -9,12 +9,13 @@
"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",
-1
View File
@@ -1 +0,0 @@
package-lock=false
+5 -3
View File
@@ -1,4 +1,4 @@
import { NexeCompiler } from 'nexe'
import { NexeCompiler, NexeOptions } from 'nexe'
import { readFileSync } from 'fs'
import { join } from 'path'
@@ -9,6 +9,8 @@ interface NexeDaemonOptions {
executable: string
}
type DaemonOptions = NexeOptions & { daemon: { windows: NexeDaemonOptions } }
function renderWinswConfig(options: any) {
return '<configuration>\r\n' +
`${Object.keys(options).reduce((config: string, element: string) => {
@@ -16,7 +18,7 @@ function renderWinswConfig(options: any) {
}, '')}</configuration>\r\n`
}
export default function daemon (compiler: NexeCompiler, next: () => Promise<void>) {
export default function daemon (compiler: NexeCompiler<DaemonOptions>, next: () => Promise<void>) {
if (compiler.target.platform !== 'windows') {
return next()
}
@@ -42,7 +44,7 @@ export default function daemon (compiler: NexeCompiler, next: () => Promise<void
'./nexe/plugin/daemon/app.js',
Buffer.from(compiler.input)
)
compiler.input = `{{replace:plugins/nexe-daemon/nexe-deamon.js}}`
compiler.input = '{{replace:plugins/nexe-daemon/lib/nexe-daemon.js}}'
return next()
}
+4
View File
@@ -0,0 +1,4 @@
console.log('hello world')
if (true) {
console.log('wat')
}
+1 -1
View File
@@ -3,7 +3,7 @@
"target": "es5",
"module": "commonjs",
"lib": [ "es2016" ],
"outDir": ".",
"outDir": "lib",
"strict": true
},
"include": [
File diff suppressed because it is too large Load Diff
+10 -15
View File
@@ -6,7 +6,7 @@ import { Readable } from 'stream'
import { spawn } from 'child_process'
import { Logger } from './logger'
import { readFileAsync, writeFileAsync, pathExistsAsync, dequote, isWindows, bound } from './util'
import { NexeOptions, nexeVersion } from './options'
import { NexeOptions, version } from './options'
import { NexeTarget } from './target'
import download = require('download')
import { getLatestGitRelease } from './releases'
@@ -22,12 +22,14 @@ 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 }
@@ -48,7 +50,7 @@ export class NexeCompiler {
: `${this.options.output || this.options.name}`
private nodeSrcBinPath: string
constructor(public options: NexeOptions) {
constructor(public options: T) {
const { python } = (this.options = options)
this.targets = options.targets as NexeTarget[]
this.target = this.targets[0]
@@ -56,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
@@ -192,7 +194,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]
@@ -206,11 +208,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)};`
}
@@ -219,7 +216,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)
@@ -228,10 +224,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
}
@@ -240,8 +236,7 @@ export class NexeCompiler {
artifact.push(chunk)
})
binary.on('close', () => {
const content = this._serializeHeader(header) + this.shims.join(';') + ';' + this.input
const content = [this.shims.join(''), this.input].join(';')
artifact.push(content)
artifact.push(this.resources.bundle)
const lengths = Buffer.from(Array(16))
+4 -10
View File
@@ -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 }
+35 -35
View File
@@ -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.7'
export const version = '{{replace:0}}'
export interface NexePatch {
(compiler: NexeCompiler, next: () => Promise<void>): Promise<void>
@@ -30,6 +30,7 @@ export interface NexeOptions {
enableNodeCli: boolean
bundle: boolean | string
patches: (string | NexePatch)[]
plugins: (string | NexePatch)[]
native: any
empty: boolean
sourceUrl?: string
@@ -61,7 +62,8 @@ const defaults = {
compress: false,
build: false,
bundle: true,
patches: []
patches: [],
plugins: []
}
const alias = {
i: 'input',
@@ -87,37 +89,37 @@ ${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[] {
@@ -203,12 +205,7 @@ 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
@@ -236,12 +233,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 = options.plugins.map(requireDefault)
options.patches = options.patches.map(requireDefault)
Object.keys(alias)
.filter(k => k !== 'rc')
@@ -250,4 +250,4 @@ function normalizeOptionsAsync(input?: Partial<NexeOptions>): Promise<NexeOption
return Promise.resolve(options)
}
export { argv, normalizeOptionsAsync }
export { argv, normalizeOptionsAsync, help }
+53
View File
@@ -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)
+1 -1
View File
@@ -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]
+1 -116
View File
@@ -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()
}
+3 -3
View File
@@ -1,11 +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(
@@ -28,9 +28,9 @@ function createBundle(options: NexeOptions) {
})
const input = relative(options.cwd, options.input).replace(/\\/g, '/')
fuse.bundle(options.name).instructions(`> ${input}`)
return fuse.run().then(x => {
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
})
}
+4 -2
View File
@@ -33,14 +33,16 @@ function readStreamAsync(stream: NodeJS.ReadableStream): PromiseLike<string> {
*/
export default async function cli(compiler: NexeCompiler, next: () => Promise<void>) {
const { log } = compiler
let stdInUsed = false
if (!process.stdin.isTTY) {
log.step('Using stdin as input')
stdInUsed = true
compiler.input = await readStreamAsync(process.stdin)
}
await next()
log.step(`Bundling: '${stdInUsed ? '[stdin]' : compiler.options.input}'`)
const target = compiler.options.targets.shift() as NexeTarget
const deliverable = await compiler.compileAsync(target)
+93
View File
@@ -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
View File
@@ -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()
}
+28
View File
@@ -0,0 +1,28 @@
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
}
return replacements[+file]
? replacements[+file]
: JSON.stringify(readFileSync(file, 'utf8'))
})
writeFileSync(filename, contents)
}