From dcea374922deffe9ae8efed087eecacd6bd2d5e2 Mon Sep 17 00:00:00 2001 From: calebboyd Date: Wed, 28 Nov 2018 20:20:33 -0600 Subject: [PATCH] fix: input detection and validity closes #556, #548, #547, #533 --- package.json | 2 +- src/compiler.ts | 9 ++++++--- src/nexe.ts | 2 +- src/options.ts | 29 +++++++++++++++++------------ src/steps/bundle.ts | 40 +++++++++++++++++++++++++++++++++++++--- src/steps/cli.ts | 39 ++++----------------------------------- src/util.ts | 1 + 7 files changed, 67 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index c34a581..741e89e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "nexe", "description": "Create a single executable out of your Node.js application", "license": "MIT", - "version": "3.0.0-beta.9", + "version": "3.0.0-beta.10", "contributors": [ "Craig Condon (http://crcn.io)", "Jared Allard ", diff --git a/src/compiler.ts b/src/compiler.ts index 06628ae..3f3d686 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1,7 +1,6 @@ import { delimiter, dirname, normalize, join } from 'path' import { Buffer } from 'buffer' import { createReadStream } from 'fs' -import { Readable, Stream } from 'stream' import { spawn } from 'child_process' import { Logger, LogStep } from './logger' import { @@ -84,6 +83,10 @@ export class NexeCompiler { * Output filename (-o myapp.exe) */ public output = this.options.output + /** + * Flag to indicate whether or notstdin was used for input + */ + public stdinUsed = false /** * Path to the configure script */ @@ -168,9 +171,9 @@ export class NexeCompiler { entry.contents = contents } - quit() { + quit(error?: any) { const time = Date.now() - this.start - this.log.write(`Finished in ${time / 1000}s`) + this.log.write(`Finished in ${time / 1000}s`, error ? 'red' : 'green') return this.log.flush() } diff --git a/src/nexe.ts b/src/nexe.ts index 0cc1c7c..bcc422d 100644 --- a/src/nexe.ts +++ b/src/nexe.ts @@ -36,10 +36,10 @@ async function compile( } if (error) { + compiler.quit(error) if (compiler.options.loglevel !== 'silent' && error) { process.stderr.write(EOL + error.message + EOL) } - compiler.quit() if (callback) return callback(error) return Promise.reject(error) } diff --git a/src/options.ts b/src/options.ts index 0c00dc9..cd57ac2 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,6 +1,6 @@ import * as parseArgv from 'minimist' import { NexeCompiler } from './compiler' -import { isWindows } from './util' +import { isWindows, STDIN_FLAG } from './util' import { basename, extname, join, isAbsolute, resolve } from 'path' import { getTarget, NexeTarget } from './target' import { EOL, homedir } from 'os' @@ -194,11 +194,16 @@ function isEntryFile(filename?: string): filename is string { return Boolean(filename && !isAbsolute(filename)) } -export function resolveEntry(input: string, cwd: string, maybeEntry?: string) { - if (input === '-' || maybeEntry === '-') { - return '' - } +export function resolveEntry( + input: string, + cwd: string, + maybeEntry: string | undefined, + bundle: boolean | string +) { let result = null + if (input === '-' || maybeEntry === '-') { + return STDIN_FLAG + } if (input && isAbsolute(input)) { return input } @@ -206,19 +211,19 @@ export function resolveEntry(input: string, cwd: string, maybeEntry?: string) { const inputPath = padRelative(input) result = resolveFileNameSync(cwd, inputPath) } - if (isEntryFile(maybeEntry) && (!result || !result.absPath)) { const inputPath = padRelative(maybeEntry) result = resolveFileNameSync(cwd, inputPath) } - if (!process.stdin.isTTY) { - return '' + if (!process.stdin.isTTY && (!result || !result.absPath) && bundle === defaults.bundle) { + return STDIN_FLAG } if (!result || !result.absPath) { result = resolveFileNameSync(cwd, '.') } - if (!result.absPath) throw new Error(`Entry file "${input}" not found!`) - + if (!result.absPath) { + throw new Error(`Entry file "${input}" not found!`) + } return result.absPath } @@ -234,8 +239,8 @@ function normalizeOptions(input?: Partial): NexeOptions { ? resolve(cwd, options.temp) : process.env.NEXE_TEMP || join(homedir(), '.nexe') const maybeEntry = isCli(input) ? argv._[argv._.length - 1] : undefined - options.input = resolveEntry(options.input, cwd, maybeEntry) - options.enableStdIn = isCli(input) && options.input === '' + options.input = resolveEntry(options.input, cwd, maybeEntry, options.bundle) + options.enableStdIn = isCli(input) && options.input === STDIN_FLAG options.name = extractName(options) options.loglevel = extractLogLevel(options) options.flags = flatten(opts.flag, options.flags) diff --git a/src/steps/bundle.ts b/src/steps/bundle.ts index 924a940..e968d05 100644 --- a/src/steps/bundle.ts +++ b/src/steps/bundle.ts @@ -1,20 +1,54 @@ import { NexeCompiler } from '../compiler' -import { resolve } from 'path' +import { resolve, relative } from 'path' import { each } from '@calebboyd/semaphore' import resolveFiles from 'resolve-dependencies' +import { dequote, STDIN_FLAG } from '../util' +import { Readable } from 'stream' + +function getStdIn(stdin: Readable): Promise { + return new Promise(resolve => { + let out = '' + stdin + .setEncoding('utf8') + .on('readable', () => { + let current + while ((current = stdin.read())) { + out += current + } + }) + .on('end', () => resolve(out)) + }) +} export default async function bundle(compiler: NexeCompiler, next: any) { const { bundle, cwd, input } = compiler.options + compiler.entrypoint = './' + relative(cwd, input) + compiler.startup = ';require("module").runMain();' + if (!bundle) { await compiler.addResource(resolve(cwd, input)) return next() } - if (!input) { + let code = '' + if (typeof bundle === 'string') { + code = await require(bundle).createBundle(compiler.options) + } + + if (input === STDIN_FLAG) { + compiler.stdinUsed = true + compiler.entrypoint = './__nexe_stdin.js' + code = code || dequote(await getStdIn(process.stdin)) + await compiler.addResource(resolve(cwd, compiler.entrypoint), code) return next() } - const { files } = await resolveFiles(input, { cwd, expand: true, loadContent: false }) + const { files, warnings } = await resolveFiles(input, { cwd, expand: true, loadContent: false }) + + if (warnings.length) { + throw new Error('Parsing Error:\n' + warnings.join('\n')) + } + await each(Object.keys(files), (filename: string) => compiler.addResource(filename), { concurrency: 10 }) diff --git a/src/steps/cli.ts b/src/steps/cli.ts index 0ed3e32..4c108a3 100644 --- a/src/steps/cli.ts +++ b/src/steps/cli.ts @@ -1,24 +1,8 @@ -import { normalize, relative, resolve } from 'path' +import { normalize, relative } from 'path' import { createWriteStream, chmodSync, statSync } from 'fs' -import { dequote } from '../util' -import { Readable } from 'stream' import { NexeCompiler } from '../compiler' import { NexeTarget } from '../target' - -function getStdIn(stdin: Readable): Promise { - return new Promise(resolve => { - let out = '' - stdin - .setEncoding('utf8') - .on('readable', () => { - let current - while ((current = stdin.read())) { - out += current - } - }) - .on('end', () => resolve(out)) - }) -} +import { STDIN_FLAG } from '../util' /** * The "cli" step detects the appropriate input. If no input options are passed, @@ -26,28 +10,13 @@ function getStdIn(stdin: Readable): Promise { * After all the build steps have run, the output (the executable) is written to a file or piped to stdout. * * Configuration: - * - compiler.options.input - file path to the input bundle. - * - fallbacks: stdin, package.json#main - * - compiler.options.output - file path to the output executable. - * - fallbacks: stdout, nexe_ + epoch + ext * * @param {*} compiler * @param {*} next */ export default async function cli(compiler: NexeCompiler, next: () => Promise) { - const { log } = compiler - let stdInUsed = false - if (!process.stdin.isTTY && compiler.options.enableStdIn) { - stdInUsed = true - compiler.entrypoint = './__nexe_stdin.js' - const code = dequote(await getStdIn(process.stdin)) - await compiler.addResource(resolve(compiler.options.cwd, compiler.entrypoint), code) - } else { - compiler.entrypoint = './' + relative(compiler.options.cwd, compiler.options.input) - } - compiler.startup = ';require("module").runMain();' await next() - + const { log } = compiler const target = compiler.options.targets.shift() as NexeTarget const deliverable = await compiler.compileAsync(target) @@ -67,7 +36,7 @@ export default async function cli(compiler: NexeCompiler, next: () => Promise( list: T[] | Promise,