feat: initial fuse-box bundling support

This commit is contained in:
calebboyd
2017-08-25 11:57:57 -05:00
parent fcd803686c
commit a0d859921d
40 changed files with 1837 additions and 1479 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ Nexe 2.0 is a rewrite to enable some new features. These include:
* Quick Builds!
* Userland build patches
* New Resource access
* stdin and stdout interfaces
* stdin interfaces
* Opt-in bundling
### Breaking Changes
+2 -3
View File
@@ -1,6 +1,5 @@
const { FuseBox } = require('fuse-box')
const nexe = '../..'
const { NativeModulePlugin } = require(nexe + '/lib/bundling/fuse')
const { NativeModulePlugin } = require('../../lib/bundling')
const fuse = FuseBox.init({
homeDir: './',
cache: false,
@@ -18,6 +17,6 @@ const fuse = FuseBox.init({
]
})
fuse.bundle('app')
.target('electron')
.target('server')
.instructions(`> index.js`)
fuse.run()
+1 -1
View File
@@ -5,6 +5,6 @@ module.exports = nexe
if (require.main === module) {
nexe.compile(nexe.argv).catch((e) => {
process.stderr.write(e.message, () => process.exit(1))
process.stderr.write(e.stack, () => process.exit(1))
})
}
+1 -2
View File
@@ -11,7 +11,7 @@ If this is a bug report, What are the steps to reproduce?
```
<br/><br/>
Please also provide:
Please also provide:
- Platform(OS/Version):
- Host Node Version:
@@ -19,4 +19,3 @@ Please also provide:
- Nexe version:
- Python Version:
+1609 -640
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -2,14 +2,14 @@
"name": "nexe",
"description": "Create a single executable out of your Node.js application",
"license": "MIT",
"version": "2.0.0-beta.7",
"version": "2.0.0-rc.1",
"contributors": [
"Craig Condon <craig.j.condon@gmail.com> (http://crcn.io)",
"Jared Allard <jaredallard@outlook.com>",
"Caleb Boyd <caleb.boyd@hotmail.com>"
],
"scripts": {
"task-build-windows": "node -e \"require('.').compile({ empty: true })\"",
"task-build": "ts-node tasks/build",
"log-node-version": "node -e \"console.log([process.platform, process.arch, process.version.slice(1)].join('-'))\"",
"prebuild": "rimraf lib && npm run lint",
"prepublish": "npm run build",
@@ -55,6 +55,7 @@
"@types/rimraf": "0.0.28",
"mocha": "^3.2.0",
"prettier": "^1.4.4",
"ts-node": "3.3.0",
"typescript": "^2.4.1"
}
}
+70
View File
@@ -1,6 +1,76 @@
import * as fs from 'fs'
import * as path from 'path'
import * as child from 'child_process'
import { createHash } from 'crypto'
export interface ExtractNodeModuleOptions {
[key: string]:
| {
additionalFiles: string[]
}
| true
}
function hashName(name: string | Buffer) {
return createHash('md5').update(name).digest('hex').toString().slice(0, 8)
}
export function embedDotNode(
options: ExtractNodeModuleOptions,
file: { contents: string; absPath: string }
) {
const contents = fs.readFileSync(file.absPath)
const module = Object.keys(options).find(x =>
Boolean(~file.absPath.indexOf(path.join('node_modules', x)))
)!
const bindingName = path.basename(file.absPath)
const settings = options[module]
const moduleDir = hashName(contents)
file.contents = `
var fs=require('fs');var path=require('path');var binding='${contents.toString(
'base64'
)}';function mkdirp(r,t){t=t||null,r=path.resolve(r);try{fs.mkdirSync(r),t=t||r}catch(c){if("ENOENT"===c.code)t=mkdirp(path.dirname(r),t),mkdirp(r,t);else{var i;try{i=fs.statSync(r)}catch(r){throw c}if(!i.isDirectory())throw c}}return t};`
if (settings === true) {
file.contents += `
mkdirp('${moduleDir}');
var bindingPath = path.join(process.cwd(), '${moduleDir}', '${bindingName}')
require('fs').writeFileSync(bindingPath, Buffer.from(binding, 'base64'))
process.dlopen(module, bindingPath)
`.trim()
return
}
let depth = 0
settings.additionalFiles.forEach(file => {
let ownDepth = 0
file.split('/').forEach(x => x === '..' && ownDepth++)
depth = ownDepth > depth ? ownDepth : depth
})
let segments = [moduleDir]
while (depth--) {
segments.push(hashName(moduleDir + depth))
}
segments.push(bindingName)
file.contents += `
var cwd = process.cwd()
var bindingFileParts = ${JSON.stringify(segments)};
var bindingFile = path.join.apply(path, [cwd].concat(bindingFileParts));
mkdirp(path.dirname(bindingFile));
fs.writeFileSync(bindingFile, Buffer.from(binding, 'base64'));
${settings.additionalFiles.reduce((code, filename, i) => {
const contents = fs.readFileSync(path.join(path.dirname(file.absPath), filename))
return (code += `
var file${i} = '${contents.toString('base64')}';
var filePath${i} = path.join(cwd, bindingFileParts[0], '${filename.split('../').join('')}');
mkdirp(path.dirname(filePath${i}));
fs.writeFileSync(filePath${i}, Buffer.from(file${i}, 'base64'));
`)
}, '')};
process.dlopen(module, bindingFile)
`
}
function findNativeModulePath(filePath: string, bindingsArg: string) {
const dirname = path.dirname(filePath)
+70
View File
@@ -0,0 +1,70 @@
import { BindingsRewrite, embedDotNode, ExtractNodeModuleOptions } from './bindings-rewrite'
export interface FuseBoxFile {
info: { absPath: string }
absPath: string
contents: string
analysis: {
dependencies: string[]
requiresRegeneration: boolean
}
loadContents(): void
makeAnalysis(
parsingOptions?: any,
traversalPlugin?: {
plugins: Array<{
onNode(file: FuseBoxFile, node: any, parent: any): void
onEnd(file: FuseBoxFile): void
}>
}
): void
}
export default function(options: ExtractNodeModuleOptions) {
return new NativeModulePlugin(options)
}
export class NativeModulePlugin {
public test: RegExp
public limit2Project = false
private modules: (keyof ExtractNodeModuleOptions)[]
constructor(public options: ExtractNodeModuleOptions) {
this.options = options
this.test = new RegExp(`node_modules\/(${Object.keys(options).join('|')}).*\.js|\.node$`)
}
init(context: any) {
context.allowExtension('.node')
}
transform(file: FuseBoxFile) {
file.loadContents()
if (file.absPath.endsWith('.node')) {
embedDotNode(this.options, file)
return
}
const bindingsRewrite = new BindingsRewrite()
file.makeAnalysis(null, {
plugins: [
{
onNode(file, node, parent) {
bindingsRewrite.onNode(file.absPath, node, parent)
},
onEnd(file) {
if (bindingsRewrite.rewrite) {
const index = file.analysis.dependencies.indexOf('bindings')
if (~index) {
file.analysis.dependencies.splice(index, 1)
}
file.analysis.dependencies.push(...bindingsRewrite.nativeModulePaths)
file.analysis.requiresRegeneration = true
}
}
}
]
})
}
}
+29 -128
View File
@@ -1,137 +1,38 @@
import { BindingsRewrite } from './bindings-rewrite'
import { createHash } from 'crypto'
import { readFileSync } from 'fs'
import { dirname, join, basename } from 'path'
import { NexeCompiler } from '../compiler'
import { FuseBox, JSONPlugin, CSSPlugin, HTMLPlugin, SourceMapPlainJsPlugin } from 'fuse-box'
//import NativeModulePlugin from './fuse-native-module-plugin'
function hashName(name: string | Buffer) {
return createHash('md5').update(name).digest('hex').toString().slice(0, 8)
function bundleProducer(filename: string, name: string) {
const fuse = FuseBox.init({
cache: false,
log: Boolean(process.env.NEXE_BUNDLE_DEBUG) || false,
homeDir: './',
sourceMaps: true,
writeBundles: false,
output: '$name.js',
target: 'server',
plugins: [JSONPlugin(), CSSPlugin(), HTMLPlugin(), SourceMapPlainJsPlugin()]
})
fuse.bundle(name).instructions(`> ${filename}`)
return fuse.run().then(x => {
let output = ''
x.bundles.forEach(y => (output = y.context.output.lastPrimaryOutput.content!.toString()))
return output
})
}
export interface FuseBoxFile {
info: { absPath: string }
absPath: string
contents: string
analysis: {
dependencies: string[]
requiresRegeneration: boolean
}
loadContents(): void
makeAnalysis(
parsingOptions?: any,
traversalPlugin?: {
plugins: Array<{
onNode(file: FuseBoxFile, node: any, parent: any): void
onEnd(file: FuseBoxFile): void
}>
}
): void
}
export interface NativeModulePluginOptions {
[key: string]:
| {
additionalFiles: string[]
}
| true
}
export default function(options: NativeModulePluginOptions) {
return new NativeModulePlugin(options)
}
export class NativeModulePlugin {
public test: RegExp
public limit2Project = false
private modules: (keyof NativeModulePluginOptions)[]
constructor(public options: NativeModulePluginOptions) {
this.modules = Object.keys(options)
this.test = new RegExp(`node_modules\/(${Object.keys(options).join('|')}).*\.js|\.node$`)
export default async function bundle(compiler: NexeCompiler, next: any) {
if (!compiler.options.bundle) {
return next()
}
init(context: any) {
context.allowExtension('.node')
let producer = bundleProducer
if (typeof compiler.options.bundle === 'string') {
producer = require(compiler.options.bundle).bundleProducer
}
transform(file: FuseBoxFile) {
file.loadContents()
compiler.input = await producer(compiler.options.input, compiler.options.name)
if (file.absPath.endsWith('.node')) {
const contents = readFileSync(file.absPath)
const module = this.modules.find(x =>
Boolean(~file.absPath.indexOf(join('node_modules', x)))
)!
const bindingName = basename(file.absPath)
const settings = this.options[module]
const moduleDir = hashName(contents)
file.contents = `
var fs=require('fs');var path=require('path');var binding='${contents.toString(
'base64'
)}';function mkdirp(r,t){t=t||null,r=path.resolve(r);try{fs.mkdirSync(r),t=t||r}catch(c){if("ENOENT"===c.code)t=mkdirp(path.dirname(r),t),mkdirp(r,t);else{var i;try{i=fs.statSync(r)}catch(r){throw c}if(!i.isDirectory())throw c}}return t};`
if (settings === true) {
file.contents += `
mkdirp('${moduleDir}');
var bindingPath = path.join(process.cwd(), '${moduleDir}', '${bindingName}')
require('fs').writeFileSync(bindingPath, Buffer.from(binding, 'base64'))
process.dlopen(module, bindingPath)
`.trim()
return
}
let depth = 0
settings.additionalFiles.forEach(file => {
let ownDepth = 0
file.split('/').forEach(x => x === '..' && ownDepth++)
depth = ownDepth > depth ? ownDepth : depth
})
let segments = [moduleDir]
while (depth--) {
segments.push(hashName(moduleDir + depth))
}
segments.push(bindingName)
file.contents += `
var cwd = process.cwd()
var bindingFileParts = ${JSON.stringify(segments)};
var bindingFile = path.join.apply(path, [cwd].concat(bindingFileParts));
mkdirp(path.dirname(bindingFile));
fs.writeFileSync(bindingFile, Buffer.from(binding, 'base64'));
${settings.additionalFiles.reduce((code, filename, i) => {
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
.split('../')
.join('')}');
mkdirp(path.dirname(filePath${i}));
fs.writeFileSync(filePath${i}, Buffer.from(file${i}, 'base64'));
`)
}, '')};
process.dlopen(module, bindingFile)
`
return
}
const bindingsRewrite = new BindingsRewrite()
file.makeAnalysis(null, {
plugins: [
{
onNode(file, node, parent) {
bindingsRewrite.onNode(file.absPath, node, parent)
},
onEnd(file) {
if (bindingsRewrite.rewrite) {
const index = file.analysis.dependencies.indexOf('bindings')
if (~index) {
file.analysis.dependencies.splice(index, 1)
}
file.analysis.dependencies.push(...bindingsRewrite.nativeModulePaths)
file.analysis.requiresRegeneration = true
}
}
}
]
})
}
require('fs').writeFileSync('./bundle.js', compiler.input)
return next()
}
+1
View File
@@ -0,0 +1 @@
export * from './fuse-native-module-plugin'
-31
View File
@@ -1,31 +0,0 @@
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') {
}
}
+9 -5
View File
@@ -18,8 +18,8 @@ function readStreamAsync(stream: NodeJS.ReadableStream): PromiseLike<string> {
}
/**
* The "cli" step detects whether the process is in a tty. If it is then the input is read into memory.
* Otherwise, it is buffered from stdin. If no input options are passed in the tty, the package.json#main file is used.
* The "cli" step detects the appropriate input. If no input options are passed,
* the package.json#main file is used.
* After all the build steps have run, the output (the executable) is written to a file or piped to stdout.
*
* Configuration:
@@ -33,10 +33,13 @@ function readStreamAsync(stream: NodeJS.ReadableStream): PromiseLike<string> {
*/
export default async function cli(compiler: NexeCompiler, next: () => Promise<void>) {
const { input, output } = compiler.options
const { log } = compiler
const { log, input: bundledInput } = compiler
if (!input && !process.stdin.isTTY) {
log.step('Using stdin as input')
compiler.input = await readStreamAsync(process.stdin)
} else if (bundledInput) {
await next()
} else if (input) {
log.step(`Using input file as the main module: ${input}`)
compiler.input = await readFileAsync(normalize(input), 'utf-8')
@@ -49,8 +52,9 @@ export default async function cli(compiler: NexeCompiler, next: () => Promise<vo
compiler.input = ''
}
compiler.input = compiler.input.trim()
await next()
if (!bundledInput) {
await next()
}
compiler.output = output || `${compiler.options.name}${isWindows ? '.exe' : ''}`
const deliverable = await compiler.compileAsync()
+2 -1
View File
@@ -33,6 +33,7 @@ export class NexeCompiler {
public src = join(this.options.temp, this.options.version)
public files: NexeFile[] = []
public input: string
public bundledInput?: string
public output: string | null
public resources: { bundle: string; index: { [key: string]: number[] } } = {
@@ -148,7 +149,7 @@ export class NexeCompiler {
}
private _serializeHeader(header: NexeHeader) {
return `/**${JSON.stringify(header)}**/process.__nexe=${JSON.stringify(header)};`
return `process.__nexe=${JSON.stringify(header)};`
}
async compileAsync() {
+2 -1
View File
@@ -3,6 +3,7 @@ import resource from './resource'
import { NexeCompiler } from './compiler'
import { argv, normalizeOptionsAsync, NexeOptions } from './options'
import cli from './cli'
import bundle from './bundling/fuse'
import download from './download'
import artifacts from './artifacts'
import patches from './patches'
@@ -25,7 +26,7 @@ async function compile(compilerOptions: NexeOptions, callback?: (err: Error | nu
}
const buildSteps = build ? [download, artifacts, ...patches, ...options.patches] : []
const nexe = compose(resource, cli, buildSteps)
const nexe = compose(resource, bundle, cli, buildSteps)
return nexe(compiler).asCallback(callback)
}
+2 -1
View File
@@ -49,6 +49,7 @@ export interface NexeOptions {
*/
temp: string
ico?: string
sourceMaps?: boolean
rc: { [key: string]: string }
/**
* Causes nexe to remove all temporary files for current configuration
@@ -183,7 +184,7 @@ function extractName(options: NexeOptions) {
function normalizeOptionsAsync(input: Partial<NexeOptions>) {
if (argv.help || argv._.some((x: string) => x === 'version')) {
process.stderr.write(argv.help ? help : '2.0.0-beta.7' + EOL, () => process.exit(0))
process.stderr.write(argv.help ? help : '2.0.0-rc.1' + EOL, () => process.exit(0))
}
const options = Object.assign({}, defaults, input) as NexeOptions
+16 -8
View File
@@ -1,6 +1,13 @@
import { NexeCompiler } from '../compiler'
import { bundleSourceMaps } from '../source-maps/build'
export default async function main(compiler: NexeCompiler, next: () => Promise<void>) {
let sourceMapSupport = ''
if (true /*compiler.options.sourceMaps*/) {
sourceMapSupport = await bundleSourceMaps()
require('fs').writeFileSync('./source-map-inject.js', sourceMapSupport)
}
await compiler.setFileContentsAsync(
'lib/_third_party_main.js',
`
@@ -10,7 +17,7 @@ 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(46))
const footer = Buffer.from(Array(32))
fs.readSync(fd, footer, 0, 32, size - 32)
if (!footer.slice(0, 16).equals(Buffer.from('<nexe~~sentinel>'))) {
@@ -92,13 +99,14 @@ if (resourceSize) {
}
}
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)
const contentBuffer = Buffer.from(Array(contentSize));
fs.readSync(fd, contentBuffer, 0, contentSize, contentStart);
fs.closeSync(fd);
${sourceMapSupport}
const Module = require('module');
process.mainModule = new Module(process.execPath, null);
process.mainModule.loaded = true;
process.mainModule._compile(contentBuffer.toString(), process.execPath);
`.trim()
)
return next()
+19
View File
@@ -0,0 +1,19 @@
import { compile } from '../'
import got = require('got')
const env = process.env,
branchName = env.CIRCLE_BRANCH || env.APPVEYOR_REPO_BRANCH,
isScheduled = Boolean(env.APPVEYOR_SCHEDULED_BUILD),
isWindows = branchName.includes('win32') && Boolean(env.APPVEYOR),
isPullRequest = Boolean(env.CIRCLE_PR_NUMBER) || Boolean(env.APPVEYOR_PULL_REQUEST_NUMBER)
async function main () {
if (isScheduled) {
const releases = await
}
}
main().catch(x => {
console.error(x)
process.exit(1)
})
-1
View File
@@ -1 +0,0 @@
true
-11
View File
@@ -1,11 +0,0 @@
/**
* Embedded Files Test
*
* @author Jared Allard <jaredallard@outlook.com>
* @version 0.0.1
* @license MIT
**/
const nexeres = require('nexeres');
process.stdout.write(nexeres.get('hw.txt').toString('ascii'));
-28
View File
@@ -1,28 +0,0 @@
{
"name": "embededfiles-test",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node index.js"
},
"dependencies": {
},
"nexe": {
"input": "index.js",
"output": "test.nex",
"resourceFiles": [
"hw.txt"
],
"browserify": {
"excludes": [],
"requires": []
},
"temp": "../src",
"debug": false,
"runtime": {
"nodeConfigureOpts": ["--fully-static"],
"framework": "node",
"version": "5.7.0"
}
}
}
-61
View File
@@ -1,61 +0,0 @@
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var jade = require('jade');
var routes = require('./routes/index');
var users = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
app.use('/users', users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
-93
View File
@@ -1,93 +0,0 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('express-test:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
process.stdout.write('true');
process.exit(0);
-28
View File
@@ -1,28 +0,0 @@
{
"name": "express-test",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"body-parser": "~1.13.2",
"cookie-parser": "~1.3.5",
"debug": "~2.2.0",
"express": "~4.13.1",
"jade": "~1.11.0",
"morgan": "~1.6.1",
"serve-favicon": "~2.3.0"
},
"nexe": {
"input": "bin/www",
"output": "test.nex",
"temp": "../src",
"debug": false,
"runtime": {
"framework": "node",
"version": "5.10.0",
"ignoreFlags": true
}
}
}
@@ -1,8 +0,0 @@
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}
-9
View File
@@ -1,9 +0,0 @@
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
-9
View File
@@ -1,9 +0,0 @@
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
module.exports = router;
-6
View File
@@ -1,6 +0,0 @@
extends layout
block content
h1= message
h2= error.status
pre #{error.stack}
-5
View File
@@ -1,5 +0,0 @@
extends layout
block content
h1= title
p Welcome to #{title}
-7
View File
@@ -1,7 +0,0 @@
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content
-13
View File
@@ -1,13 +0,0 @@
/**
* Test to verify if we support strict mode (flags)
*
* @author Jared Allard <jaredallard@outlook.com>
* @version 0.0.1
* @license MIT
**/
// "use strict"; w/o --use_strict
let isStrict = (function() { return !this; })();
console.log(isStrict);
-15
View File
@@ -1,15 +0,0 @@
{
"name": "flags-test",
"nexe": {
"input": "./index.js",
"output": "test.nex",
"temp": "../src",
"runtime": {
"framework": "node",
"version": "5.7.0",
"ignoreFlags": false,
"js-flags": "--use_strict",
"node-args": ""
}
}
}
-23
View File
@@ -1,23 +0,0 @@
"use strict";
let gulp = require('gulp');
let nexe = require('nexe');
gulp.task( "compile", ( callback ) => {
let options = {
input: "index.js",
output: "test.nex",
python: "python",
nodeTempDir: "./tmp",
nodeVersion: "latest",
flags: false,
framework: "nodejs"
};
nexe.compile( options, ( err ) => {
console.log( err );
callback( err );
} );
} );
gulp.task('default', ['compile']);
-2
View File
@@ -1,2 +0,0 @@
// it probably executed.
console.log(true);
-26
View File
@@ -1,26 +0,0 @@
{
"name": "gulp-test-170",
"version": "0.0.1",
"description": "gulpfile.js issue #170",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Jared Allard <jaredallard@outlook.com>",
"license": "MIT",
"dependencies": {
"gulp": "^3.9.0",
"nexe": "^1.0.5"
},
"nexe": {
"input": "./index.js",
"output": "test.nex",
"temp": "../src",
"runtime": {
"framework": "node",
"version": "5.7.0",
"ignoreFlags": true,
"node-args": ""
}
}
}
-19
View File
@@ -1,19 +0,0 @@
/**
* Test to verify if we support strict mode (flags)
*
* @author Jared Allard <jaredallard@outlook.com>
* @version 0.0.1
* @license MIT
**/
// test.nex --help
let status = false;
// console.log(process.argv);
if(process.argv[2]) {
status = true;
}
console.log(status);
-15
View File
@@ -1,15 +0,0 @@
{
"name": "flags-test",
"nexe": {
"input": "./index.js",
"output": "test.nex",
"temp": "../src",
"runtime": {
"framework": "node",
"version": "5.7.0",
"ignoreFlags": true,
"node-args": ""
}
},
"dependencies": {}
}
-3
View File
@@ -1,3 +0,0 @@
require('js-yaml');
console.log(true);
-23
View File
@@ -1,23 +0,0 @@
{
"name": "js-yaml-148",
"version": "1.0.0",
"description": "#148 js-yaml fails to work",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Jared Allard <jaredallard@outlook.com>",
"license": "MIT",
"dependencies": {
"js-yaml": "^3.5.2"
},
"nexe": {
"input": "index.js",
"output": "test.nex",
"temp": "../src",
"runtime": {
"version": "5.7.0",
"framework": "node"
}
}
}
-1
View File
@@ -1 +0,0 @@
--timeout 100000000
-249
View File
@@ -1,249 +0,0 @@
/**
* Test runner for nexe
*
* @author Jared Allard <jaredallard@outlook.com>
* @license MIT
**/
'use strict';
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const spawn = require('child_process').spawn;
// our vars
const NEXE_VERSION = require('../package.json').version;
const testBase = __dirname;
const LOG_FILE = path.join(testBase, 'test.log');
if(fs.existsSync(LOG_FILE)) {
fs.unlinkSync(LOG_FILE);
}
let logfile = fs.createWriteStream(LOG_FILE, {
autoClose: false
});
const compileTest = function(test, cb) {
let testDir = path.join(testBase, test);
if(!fs.existsSync(testDir)) {
throw new Error('Test not found');
return cb(false);
}
let npminst = spawn('npm', ['install'], {
cwd: testDir
});
npminst.on('error', function() {
throw new Error('Failed to execute NPM');
return cb(false);
});
npminst.on('close', function(code) {
if(code !== 0) {
throw new Error('NPM exited with non-zero status');
return cb(false);
}
let testinst = spawn('../../bin/nexe', [], {
cwd: testDir
});
testinst.on('error', function() {
throw new Error('Test failed to compile');
return cb(false);
});
testinst.on('close', function(code) {
let err = null;
if(code !== 0) {
throw new Error('Test gave non-zero code');
err = false;
}
logfile = fs.createWriteStream(LOG_FILE);
return cb(err);
});
testinst.stdout.on('data', function(d) {
process.stdout.write(d.toString('ascii'));
});
});
};
/**
* Run a test-generated binary
*
* @param {string} test - test name (dir)
* @param {array} optional args - array of args to pass to the binary
* @param {function} cb - callback
**/
const runTest = function(test, args, cb) {
let testDir = path.join(testBase, test);
let testBin = path.join(testDir, 'test.nex')
// check params
if(!Array.isArray(args)) {
cb = args;
args = [];
}
if(!fs.existsSync(testDir)) {
throw new Error('Test not found');
return cb(false);
}
if(!fs.existsSync(testBin)) {
throw new Error('Test binary not found');
return cb(false);
}
let returned = false;
let testinst = spawn(testBin, args, {
cwd: testDir
})
testinst.stdout.on('data', function(d) {
let status = d.toString('ascii');
if(status === true || status === 'true' || status === 'true\n') {
testinst.stdin.pause();
testinst.kill();
// make it know we returned.
returned = true;
return cb();
}
})
testinst.on('close', function(c) {
if(returned !== true) {
throw new Error('Test failed (stdout never contained true)');
return cb(false);
}
});
}
console.log('NOTICE: The first test may take awhile as it may compile Node.js');
console.log(' Monitor the log file (test.log) for progress.');
after(function() {
console.log('notice: closing test.log file descriptor.')
logfile.end();
});
/**
* Tests express compatability.
**/
describe('nexe can bundle express', function() {
let testname = 'express-test';
describe('build', function () {
it('compiles without errors', function (next) {
compileTest(testname, function(err) {
return next(err);
});
});
it('runs successfully', function(next) {
runTest(testname, function(err) {
return next(err);
});
});
});
});
describe('nexe and embedding files', function() {
let testname = 'embeded-files';
describe('build', function () {
it('compiles without errors', function (next) {
compileTest(testname, function(err) {
return next(err);
});
});
it('runs successfully', function(next) {
runTest(testname, function(err) {
return next(err);
});
});
});
});
describe('v8 flags test (strict mode)', function() {
let testname = 'flags-test';
describe('build', function () {
it('compiles without errors', function (next) {
compileTest(testname, function(err) {
return next(err);
});
});
it('runs successfully', function(next) {
runTest(testname, function(err) {
return next(err);
});
});
});
});
describe('nexe can be utilized in gulp test', function() {
let testname = 'gulp-test-170';
describe('build', function () {
it('compiles without errors', function (next) {
compileTest(testname, function(err) {
return next(err);
});
});
it('runs successfully', function(next) {
runTest(testname, function(err) {
return next(err);
});
});
});
});
describe('nexe can ignore node.js flags test', function() {
let testname = 'ignoreFlags-test';
describe('build', function () {
it('compiles without errors', function (next) {
compileTest(testname, function(err) {
return next(err);
});
});
it('runs successfully', function(next) {
runTest(testname, ['--help'], function(err) {
return next(err);
});
});
});
});
describe('nexe supports js-yaml test', function() {
let testname = 'js-yaml-148';
describe('build', function () {
it('compiles without errors', function (next) {
compileTest(testname, function(err) {
return next(err);
});
});
it('runs successfully', function(next) {
runTest(testname, function(err) {
return next(err);
});
});
});
});