diff --git a/.gitignore b/.gitignore index dc14482..580e70a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store temp/ +coverage node_modules *.log *.exe diff --git a/package.json b/package.json index 0c0ecb2..bf6acf2 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,11 @@ "Caleb Boyd " ], "scripts": { - "build": "babel src -d lib", + "clean": "rimraf lib", + "prebuild": "npm run clean && npm run lint", + "build": "babel src -d lib --copy-files", "lint": "standard index.js ./src/**/* --fix", - "test": "npm run lint" + "test": "mocha" }, "repository": { "type": "git", diff --git a/src/artifacts.js b/src/artifacts.js index e53e7a5..ff09641 100644 --- a/src/artifacts.js +++ b/src/artifacts.js @@ -1,5 +1,6 @@ import { join, dirname } from 'path' -import { readdir, stat, readFile, unlink, writeFile } from 'fs' +import { readdir, stat, unlink } from 'fs' +import { readFileAsync, writeFileAsync } from './util' import { promisify, map } from 'bluebird' import mkdirp from 'mkdirp' @@ -7,8 +8,6 @@ const mkdirpAsync = promisify(mkdirp) const statAsync = promisify(stat) const unlinkAsync = promisify(unlink) const readdirAsync = promisify(readdir) -const writeAnyFileAsync = promisify(writeFile) -const readFileAsync = promisify(readFile) function readDirAsync (dir) { return readdirAsync(dir).map((file) => { @@ -37,25 +36,26 @@ function maybeReadFileContents (file) { * - Finally, The patched files are written into source. * */ -export async function artifacts ({ files, writeFileAsync, src }, next) { +export default async function artifacts (compiler, next) { + const { src } = compiler const temp = join(src, 'nexe') await mkdirpAsync(temp) const tmpFiles = await readDirAsync(temp) await map(tmpFiles, async (path) => { - return writeFileAsync(path.replace(temp, ''), await readFileAsync(path)) + return compiler.writeFileAsync(path.replace(temp, ''), await readFileAsync(path)) }) await next() await map(tmpFiles, x => unlinkAsync(x)) - return map(files, async (file) => { + return map(compiler.files, async (file) => { const sourceFile = join(src, file.filename) const tempFile = join(temp, file.filename) const fileContents = await maybeReadFileContents(sourceFile) await mkdirpAsync(dirname(tempFile)) - await writeAnyFileAsync(tempFile, fileContents) - await writeFileAsync(file.filename, file.contents) + await writeFileAsync(tempFile, fileContents) + await compiler.writeFileAsync(file.filename, file.contents) }) } diff --git a/src/bundle.js b/src/bundle.js index b063dbe..88bdfbb 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -3,21 +3,19 @@ import webpack from 'webpack' import { fromCallback } from 'bluebird' import { resolve, join, relative } from 'path' -const - isObject = (x) => typeof x === 'object', - isString = (x) => typeof x === 'string' +const isObject = (x) => typeof x === 'object' +const isString = (x) => typeof x === 'string' -export async function bundle (compiler, next) { +export default async function bundle (compiler, next) { let bundleConfig = compiler.options.bundle if (!bundleConfig) { return next() } - const - input = compiler.options.input || require.resolve(process.cwd()), - fs = new Mfs(), - path = resolve('nexe'), - filename = 'virtual-bundle.js' + const input = compiler.options.input || require.resolve(process.cwd()) + const mfs = new Mfs() + const path = resolve('nexe') + const filename = 'virtual-bundle.js' if (isString(bundleConfig)) { bundleConfig === require(relative(process.cwd(), bundleConfig)) @@ -30,15 +28,15 @@ export async function bundle (compiler, next) { } const bundler = webpack(bundleConfig) - bundler.outputFileSystem = fs + bundler.outputFileSystem = mfs const stats = await fromCallback(cb => bundler.run(cb)) if (stats.hasErrors()) { compiler.log.error(stats.toString()) return null } - //eslint-disable-next-line no-sync - compiler.input = fs.readFileSync( + + compiler.input = mfs.readFileSync( join(bundleConfig.output.path, bundleConfig.output.filename) ) diff --git a/src/cli.js b/src/cli.js index a163dfe..567e9d2 100644 --- a/src/cli.js +++ b/src/cli.js @@ -1,9 +1,8 @@ import { normalize } from 'path' -import { Promise, promisify } from 'bluebird' -import { createWriteStream, readFile } from 'fs' -import { dequote } from './util' +import { Promise } from 'bluebird' +import { createWriteStream } from 'fs' +import { dequote, readFileAsync } from './util' -const readFileAsync = promisify(readFile) const isWindows = process.platform === 'win32' function getStdIn () { @@ -32,7 +31,7 @@ function getStdIn () { * @param {*} compiler * @param {*} next */ -export async function cli (compiler, next) { +export default async function cli (compiler, next) { const input = compiler.options.input const bundled = Boolean(compiler.input) diff --git a/src/compiler.js b/src/compiler.js index be7f11d..c3a12b3 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -1,21 +1,18 @@ import { normalize, join } from 'path' -import { promisify, Promise } from 'bluebird' -import { normalizeOptions } from './options' -import { readFile, writeFile, createReadStream } from 'fs' +import { Promise } from 'bluebird' +import { createReadStream } from 'fs' import { spawn } from 'child_process' import * as logger from './logger' -import { dequote } from './util' +import { readFileAsync, writeFileAsync, dequote } from './util' const isWindows = process.platform === 'win32' const isBsd = Boolean(~process.platform.indexOf('bsd')) const make = isWindows ? 'vcbuild.bat' : isBsd ? 'gmake' : 'make' const configure = isWindows ? 'configure' : './configure' -const readFileAsync = promisify(readFile) -const writeFileAsync = promisify(writeFile) export class NexeCompiler { constructor (options) { - options = this.options = normalizeOptions(options) + this.options = options logger.setLevel(options.loglevel) this.log = logger this.python = options.python @@ -26,20 +23,16 @@ export class NexeCompiler { this.files = [] this.readFileAsync = async (file) => { - const cachedFile = this.files.find(x => normalize(x.filename) === normalize(file)) - if (cachedFile) { - return Promise.resolve(cachedFile) + let cachedFile = this.files.find(x => normalize(x.filename) === normalize(file)) + if (!cachedFile) { + cachedFile = { + filename: file, + contents: await readFileAsync(join(this.src, file), 'utf-8') + .catch({ code: 'ENOENT' }, () => '') + } + this.files.push(cachedFile) } - this.files.push({ - filename: file, - contents: await readFileAsync(join(this.src, file), 'utf-8').catch(e => { - if (e.code !== 'ENOENT') { - throw e - } - return '' - }) - }) - return this.readFileAsync(file) + return cachedFile } this.writeFileAsync = (file, contents) => writeFileAsync(join(this.src, file), contents) } @@ -80,9 +73,9 @@ export class NexeCompiler { ) } - buildAsync () { - return this._configureAsync() - .then(() => this._runBuildCommandAsync(make, this.make)) + async buildAsync () { + await this._configureAsync() + return this._runBuildCommandAsync(make, this.make) } getDeliverableAsync () { diff --git a/src/download.js b/src/download.js index 7444595..5267513 100644 --- a/src/download.js +++ b/src/download.js @@ -56,7 +56,7 @@ function cleanSrc (clean, src, log) { * @param {*} compiler * @param {*} next */ -export function download (compiler, next) { +export default function download (compiler, next) { const { src, log } = compiler const { version, sourceUrl, clean } = compiler.options const url = sourceUrl || `https://nodejs.org/dist/v${version}/node-v${version}.tar.gz` diff --git a/src/nexe.js b/src/nexe.js index 6a4e9f4..6fb4e37 100644 --- a/src/nexe.js +++ b/src/nexe.js @@ -1,19 +1,21 @@ -import { compose } from 'app-builder' -import { bundle } from './bundle' +import { compose, PromiseConfig } from 'app-builder' +import bundle from './bundle' import { NexeCompiler } from './compiler' -import { argv } from './options' -import { cli } from './cli' -import { download } from './download' -import { artifacts } from './artifacts' -import { patches } from './patches' +import { argv, normalizeOptionsAsync } from './options' +import cli from './cli' +import download from './download' +import artifacts from './artifacts' +import patches from './patches' import { EOL } from 'os' import { longStackTraces, Promise } from 'bluebird' import { error } from './logger' +PromiseConfig.constructor = Promise longStackTraces() -export function compile (compilerOptions, callback) { - const compiler = new NexeCompiler(compilerOptions) +async function compile (compilerOptions, callback) { + const options = await normalizeOptionsAsync(compilerOptions) + const compiler = new NexeCompiler(options) compiler.log.verbose('Compiler options:' + EOL + JSON.stringify(compiler.options, null, 4) @@ -23,28 +25,30 @@ export function compile (compilerOptions, callback) { bundle, cli, download, - (nexeCompiler, next) => { - return next().then(() => { - return nexeCompiler.buildAsync() - }) + async (_, next) => { + await next() + return compiler.buildAsync() }, artifacts, patches, compiler.options.patches ) - return Promise.resolve(nexe(compiler)).asCallback(callback) + return nexe(compiler).asCallback(callback) } -export function isNexe (callback) { +function isNexe (callback) { return Promise.resolve(Boolean(process.__nexe)).asCallback(callback) } export { - argv + argv, + isNexe, + compile } if (require.main === module || process.__nexe) { - compile(argv).catch((e) => { - error(e.stack, () => process.exit(1)) - }) + compile(argv) + .catch((e) => { + error(e.stack, () => process.exit(e.exitCode || 1)) + }) } diff --git a/src/options.js b/src/options.js index 448de46..0701746 100644 --- a/src/options.js +++ b/src/options.js @@ -1,12 +1,10 @@ import parseArgv from 'minimist' import { basename, extname, join } from 'path' +import { fromCallback, Promise } from 'bluebird' import { EOL } from 'os' function padRight (str, l) { - while (str.length < l) { - str += ' ' - } - return str.substr(0, l) + return (str + ' '.repeat(l)).substr(0, l) } const defaults = { temp: process.env.NEXE_TEMP || join(process.cwd(), '.nexe'), @@ -93,41 +91,57 @@ function extractCliMap (match, options) { }, null) } +function tryResolveMainFileName () { + let filename = 'nexe' + try { + const file = require.resolve(process.cwd()) + filename = basename(file).replace(extname(file), '') + } catch (_) {} + + return filename === 'index' ? ('nexe_' + Date.now()) : filename +} + +function extractLogLevel (options) { + if (options.loglevel) return options.loglevel + if (options.silent) return 'silent' + if (options.verbose) return 'verbose' + if (options.info) return 'info' +} + function extractName (options) { if (typeof options.input === 'string') { return options.name || basename(options.input).replace(extname(options.input), '') } - const mainName = basename(require.resolve(process.cwd())) - return options.name || mainName.replace(extname(mainName), '') + const mainName = tryResolveMainFileName() + return options.name || mainName } -export function normalizeOptions (input) { +function normalizeOptionsAsync (input) { if (argv.help || Boolean(argv._.find(x => x === 'version'))) { - process.stderr.write( - argv.help ? help : 'next' + EOL, - () => process.exit(0) - ) + return fromCallback(cb => process.stderr.write( + argv.help ? help : '2.0.0-beta.1' + EOL, + () => cb(null, process.exit(1)) + )) } const options = Object.assign({}, defaults, input) delete options._ - delete alias.rc - options.loglevel = options.loglevel ? options.loglevel - : options.silent ? 'silent' - : options.verbose ? 'verbose' - : 'info' + options.loglevel = extractLogLevel(options) options.name = extractName(options) options.flags = flattenFilter(options.flag, options.flags) options.make = flattenFilter(options.make) options.vcBuild = flattenFilter(options.vcBuild) options.rc = options.rc || extractCliMap(/^rc-.*/, options) options.resources = options.resources || extractCliMap(/^resource-.*/, options) - Object.keys(alias).forEach(x => delete options[x]) + Object.keys(alias) + .filter(k => k !== 'rc') + .forEach(x => delete options[x]) - return options + return Promise.resolve(options) } export { - argv + argv, + normalizeOptionsAsync } diff --git a/src/patches/content.js b/src/patches/content.js index b1e66e0..34d792b 100644 --- a/src/patches/content.js +++ b/src/patches/content.js @@ -1,6 +1,6 @@ import { Buffer } from 'buffer' -export async function content (compiler, next) { +export default async function content (compiler, next) { await next() const filename = 'lib/' + compiler.options.name + '.js' diff --git a/src/patches/disable-node-cli.js b/src/patches/disable-node-cli.js index defccb9..4a9903b 100644 --- a/src/patches/disable-node-cli.js +++ b/src/patches/disable-node-cli.js @@ -1,4 +1,4 @@ -export async function disableNodeCli (compiler, next) { +export default async function disableNodeCli (compiler, next) { if (compiler.options.enableNodeCli) { return next() } diff --git a/src/patches/flags.js b/src/patches/flags.js index 4392b0f..3e1d013 100644 --- a/src/patches/flags.js +++ b/src/patches/flags.js @@ -1,4 +1,4 @@ -export async function flags (compiler, next) { +export default async function flags (compiler, next) { const nodeflags = compiler.options.flags if (!nodeflags.length) { return next() diff --git a/src/patches/gyp.js b/src/patches/gyp.js index 55c4719..f666d72 100644 --- a/src/patches/gyp.js +++ b/src/patches/gyp.js @@ -1,4 +1,4 @@ -export async function nodeGyp ({ files, readFileAsync, download }, next) { +export default async function nodeGyp ({ files, readFileAsync }, next) { await next() const nodegyp = await readFileAsync('node.gyp') diff --git a/src/patches/ico.js b/src/patches/ico.js index f3eb23d..c9551a9 100644 --- a/src/patches/ico.js +++ b/src/patches/ico.js @@ -1,10 +1,7 @@ -import { readFile } from 'fs' import { normalize } from 'path' -import { promisify } from 'bluebird' +import { readFileAsync } from '../util' -const readFileAsync = promisify(readFile) - -export async function ico (compiler, next) { +export default async function ico (compiler, next) { const iconFile = compiler.options.ico if (!iconFile) { return next() diff --git a/src/patches/index.js b/src/patches/index.js index d1cde8c..3e38adc 100644 --- a/src/patches/index.js +++ b/src/patches/index.js @@ -1,9 +1,19 @@ -module.exports.patches = [ - require('./gyp').nodeGyp, // upstream, sets manifest - require('./content').content, // upstream, sets main module content - require('./third-party-main').main, // loads main module - require('./disable-node-cli').disableNodeCli, - require('./flags').flags, - require('./ico').ico, - require('./node-rc').nodeRc +import gyp from './gyp' +import content from './content' +import main from './third-party-main' +import cli from './disable-node-cli' +import flags from './flags' +import ico from './ico' +import rc from './node-rc' + +const patches = [ + gyp, + content, + main, + cli, + flags, + ico, + rc ] + +export default patches diff --git a/src/patches/node-rc.js b/src/patches/node-rc.js index e4d9967..c3e09ad 100644 --- a/src/patches/node-rc.js +++ b/src/patches/node-rc.js @@ -1,4 +1,4 @@ -export async function nodeRc (compiler, next) { +export default async function nodeRc (compiler, next) { const options = compiler.options.rc if (!options) { return next() diff --git a/src/patches/snapshot.js b/src/patches/snapshot.js index 676ae9c..703b59f 100644 --- a/src/patches/snapshot.js +++ b/src/patches/snapshot.js @@ -1,4 +1,4 @@ -export async function snapshot (compiler, next) { +export default async function snapshot (compiler, next) { const snapshotFile = compiler.options.snapshot if (!snapshotFile) { diff --git a/src/patches/third-party-main.js b/src/patches/third-party-main.js index 8f37f0c..fdbdd3d 100644 --- a/src/patches/third-party-main.js +++ b/src/patches/third-party-main.js @@ -1,4 +1,4 @@ -export async function main (compiler, next) { +export default async function main (compiler, next) { const mainFile = await compiler.readFileAsync('lib/_third_party_main.js') mainFile.contents = ` Object.defineProperty(process, '__nexe', { diff --git a/src/util.js b/src/util.js index 50a5bcf..881c2b9 100644 --- a/src/util.js +++ b/src/util.js @@ -1,4 +1,7 @@ -export function dequote (input) { +import { readFile, writeFile } from 'fs' +import { promisify } from 'bluebird' + +function dequote (input) { input = input.trim() const singleQuote = input.startsWith('\'') && input.endsWith('\'') @@ -8,3 +11,12 @@ export function dequote (input) { } return input } + +const readFileAsync = promisify(readFile) +const writeFileAsync = promisify(writeFile) + +export { + readFileAsync, + writeFileAsync, + dequote +}