Initial commit for working module only with api change

This commit is contained in:
Jia Hao 2016-01-18 21:45:18 +08:00
parent 5f20dca5cc
commit 5379740dda
29 changed files with 245 additions and 1414 deletions

25
.editorconfig Normal file
View File

@ -0,0 +1,25 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,py}]
charset = utf-8
indent_style = space
indent_size = 4
# 2 space indentation
[*.{html,css,less,scss,yml,json}]
indent_style = space
indent_size = 2
# Tab indentation (no size specified)
[Makefile]
indent_style = tab

83
.gitignore vendored
View File

@ -1,52 +1,43 @@
node_modules
# OSX
.DS_Store
build/
# Node.js
# ignore compiled lib files
lib
dist
# Logs
logs
*.log
npm-debug.log*
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules
# IntelliJ project files
.idea
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
out
gen

View File

@ -1,3 +0,0 @@
language: node_js
node_js:
- '0.12'

View File

@ -1,9 +0,0 @@
Please provide the following information when opening issues:
- Which version of electron-packager are you using?
- What cli arguments are you passing?
- What platform are you running electron-packager on? What platform(s) are you building for?
- Is there a stack trace in the error message you're seeing?
- If possible, please provide instructions to reproduce your problem
Thanks!

23
LICENSE
View File

@ -1,23 +0,0 @@
Copyright (c) 2015 Max Ogden
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

55
cli.js
View File

@ -1,55 +0,0 @@
#!/usr/bin/env node
var fs = require('fs');
var args = require('minimist')(process.argv.slice(2), {boolean: ['prune', 'asar', 'all', 'overwrite']});
var packager = require('./');
var usage = fs.readFileSync(__dirname + '/usage.txt').toString();
var validator = require('validator');
var tempDir = require('./tempDir');
args.dir = 'blah'; // set to true first
args.name = args._[0];
args.target = args._[1];
var protocolSchemes = [].concat(args.protocol || []);
var protocolNames = [].concat(args['protocol-name'] || []);
if (protocolSchemes && protocolNames && protocolNames.length === protocolSchemes.length) {
args.protocols = protocolSchemes.map(function (scheme, i) {
return {schemes: [scheme], name: protocolNames[i]};
})
}
if (!args.dir || !args.name || !args.version || !args.target || (!args.all && (!args.platform || !args.arch))) {
console.error(usage);
process.exit(1);
}
if (!validator.isURL(args.target)) {
console.error('Enter a valid target url');
process.exit(1);
}
tempDir(args.name, args.target, args.badge, args.width, args.height, function (error, appDir) {
if (error) {
console.error(error);
process.exit(1);
} else {
args.dir = appDir;
packager(args, function done(err, appPaths) {
if (err) {
if (err.message) console.error(err.message);
else console.error(err, err.stack);
process.exit(1);
}
if (appPaths.length > 1) console.error('Wrote new apps to:\n' + appPaths.join('\n'));
else if (appPaths.length === 1) console.error('Wrote new app to', appPaths[0]);
});
}
});

View File

@ -1,15 +0,0 @@
## Collaborators
electron-packager is only possible due to the excellent work of the following collaborators:
<table><tbody><tr><th align="left">malept</th><td><a href="https://github.com/malept">GitHub/malept</a></td></tr>
<tr><th align="left">maxogden</th><td><a href="https://github.com/maxogden">GitHub/maxogden</a></td></tr>
<tr><th align="left">shama</th><td><a href="https://github.com/shama">GitHub/shama</a></td></tr>
<tr><th align="left">feross</th><td><a href="https://github.com/feross">GitHub/feross</a></td></tr>
<tr><th align="left">sindresorhus</th><td><a href="https://github.com/sindresorhus">GitHub/sindresorhus</a></td></tr>
<tr><th align="left">mafintosh</th><td><a href="https://github.com/mafintosh">GitHub/mafintosh</a></td></tr>
<tr><th align="left">kfranqueiro</th><td><a href="https://github.com/kfranqueiro">GitHub/kfranqueiro</a></td></tr>
<tr><th align="left">jden</th><td><a href="https://github.com/jden">GitHub/jden</a></td></tr>
<tr><th align="left">stefanbuck</th><td><a href="https://github.com/stefanbuck">GitHub/stefanbuck</a></td></tr>
<tr><th align="left">remixz</th><td><a href="https://github.com/remixz">GitHub/remixz</a></td></tr>
</tbody></table>

139
common.js
View File

@ -1,139 +0,0 @@
var child = require('child_process')
var fs = require('fs')
var os = require('os')
var path = require('path')
var asar = require('asar')
var mkdirp = require('mkdirp')
var ncp = require('ncp').ncp
var rimraf = require('rimraf')
var series = require('run-series')
function asarApp (appPath, cb) {
var src = path.join(appPath)
var dest = path.join(appPath, '..', 'app.asar')
asar.createPackage(src, dest, function (err) {
if (err) return cb(err)
rimraf(src, function (err) {
if (err) return cb(err)
cb(null, dest)
})
})
}
function generateFinalBasename (opts) {
return opts.name + '-' + opts.platform + '-' + opts.arch
}
function generateFinalPath (opts) {
return path.join(opts.out || process.cwd(), generateFinalBasename(opts))
}
function userIgnoreFilter (opts) {
return function filter (file) {
file = file.split(path.resolve(opts.dir))[1]
if (path.sep === '\\') {
// convert slashes so unix-format ignores work
file = file.replace(/\\/g, '/')
}
var ignore = opts.ignore || []
if (!Array.isArray(ignore)) ignore = [ignore]
for (var i = 0; i < ignore.length; i++) {
if (file.match(ignore[i])) {
return false
}
}
return true
}
}
module.exports = {
generateFinalPath: generateFinalPath,
initializeApp: function initializeApp (opts, templatePath, appRelativePath, callback) {
// Performs the following initial operations for an app:
// * Creates temporary directory
// * Copies template into temporary directory
// * Copies user's app into temporary directory
// * Prunes non-production node_modules (if opts.prune is set)
// * Creates an asar (if opts.asar is set)
var tempParent = path.join(os.tmpdir(), 'electron-packager', opts.platform + '-' + opts.arch)
var tempPath = path.join(tempParent, generateFinalBasename(opts))
// Path to `app` directory
var appPath = path.join(tempPath, appRelativePath)
var operations = [
function (cb) {
rimraf(tempParent, function () {
// Ignore errors (e.g. directory didn't exist anyway)
cb()
})
},
function (cb) {
mkdirp(tempPath, cb)
},
function (cb) {
ncp(templatePath, tempPath, cb)
},
function (cb) {
ncp(opts.dir, appPath, {filter: userIgnoreFilter(opts), dereference: true}, cb)
}
]
// Prune and asar are now performed before platform-specific logic, primarily so that
// appPath is predictable (e.g. before .app is renamed for mac)
if (opts.prune) {
operations.push(function (cb) {
child.exec('npm prune --production', {cwd: appPath}, cb)
})
}
if (opts.asar) {
operations.push(function (cb) {
asarApp(path.join(appPath), cb)
})
}
series(operations, function (err) {
if (err) return callback(err)
// Resolve to path to temporary app folder for platform-specific processes to use
callback(null, tempPath)
})
},
moveApp: function finalizeApp (opts, tempPath, callback) {
var finalPath = generateFinalPath(opts)
// Prefer ncp over mv (which seems to cause issues on Win8)
series([
function (cb) {
mkdirp(finalPath, cb)
},
function (cb) {
ncp(tempPath, finalPath, cb)
}
], function (err) {
callback(err, finalPath)
})
},
normalizeExt: function normalizeExt (filename, targetExt, cb) {
// Forces a filename to a given extension and fires the given callback with the normalized filename,
// if it exists. Otherwise reports the error from the fs.stat call.
// (Used for resolving icon filenames, particularly during --all runs.)
// This error path is used by win32.js if no icon is specified
if (!filename) return cb(new Error('No filename specified to normalizeExt'))
var ext = path.extname(filename)
if (ext !== targetExt) {
filename = filename.slice(0, filename.length - ext.length) + targetExt
}
fs.stat(filename, function (err) {
cb(err, err ? null : filename)
})
}
}

169
index.js
View File

@ -1,169 +0,0 @@
var path = require('path')
var fs = require('fs')
var os = require('os')
var download = require('electron-download')
var extract = require('extract-zip')
var mkdirp = require('mkdirp')
var rimraf = require('rimraf')
var series = require('run-series')
var common = require('./common')
var supportedArchs = {
ia32: 1,
x64: 1
}
var supportedPlatforms = {
// Maps to module ID for each platform (lazy-required if used)
darwin: './mac',
linux: './linux',
win32: './win32'
}
var tempBase = path.join(os.tmpdir(), 'electron-packager')
function testSymlink (cb) {
var testPath = path.join(tempBase, 'symlink-test')
var testFile = path.join(testPath, 'test')
var testLink = path.join(testPath, 'testlink')
series([
function (cb) {
mkdirp(testPath, cb)
},
function (cb) {
fs.writeFile(testFile, '', cb)
},
function (cb) {
fs.symlink(testFile, testLink, cb)
}
], function (err) {
var result = !err
rimraf(testPath, function () {
cb(result) // ignore errors on cleanup
})
})
}
function validateList (list, supported, name) {
// Validates list of architectures or platforms.
// Returns a normalized array if successful, or an error message string otherwise.
if (!list) return 'Must specify ' + name
if (list === 'all') return Object.keys(supported)
if (!Array.isArray(list)) list = list.split(',')
for (var i = list.length; i--;) {
if (!supported[list[i]]) {
return 'Unsupported ' + name + ' ' + list[i] + '; must be one of: ' + Object.keys(supported).join(', ')
}
}
return list
}
function createSeries (opts, archs, platforms) {
var combinations = []
archs.forEach(function (arch) {
platforms.forEach(function (platform) {
// Electron does not have 32-bit releases for Mac OS X, so skip that combination
if (platform === 'darwin' && arch === 'ia32') return
combinations.push({
platform: platform,
arch: arch,
version: opts.version
})
})
})
return [
function (cb) {
rimraf(tempBase, cb)
}
].concat(combinations.map(function (combination) {
var arch = combination.arch
var platform = combination.platform
var version = combination.version
return function (callback) {
download(combination, function (err, zipPath) {
if (err) return callback(err)
var tmpDir = path.join(tempBase, platform + '-' + arch + '-template')
var operations = [
function (cb) {
mkdirp(tmpDir, cb)
},
function (cb) {
extract(zipPath, {dir: tmpDir}, cb)
}
]
function createApp (comboOpts) {
console.error('Packaging app for platform', platform + ' ' + arch, 'using electron v' + version)
series(operations, function () {
require(supportedPlatforms[platform]).createApp(comboOpts, tmpDir, callback)
})
}
function checkOverwrite () {
// Create delegated options object with specific platform and arch, for output directory naming
var comboOpts = Object.create(opts)
comboOpts.arch = arch
comboOpts.platform = platform
var finalPath = common.generateFinalPath(comboOpts)
fs.exists(finalPath, function (exists) {
if (exists) {
if (opts.overwrite) {
rimraf(finalPath, function () {
createApp(comboOpts)
})
} else {
console.error('Skipping ' + platform + ' ' + arch +
' (output dir already exists, use --overwrite to force)')
callback()
}
} else {
createApp(comboOpts)
}
})
}
if (combination.platform === 'darwin') {
testSymlink(function (result) {
if (result) return checkOverwrite()
console.error('Cannot create symlinks; skipping darwin platform')
callback()
})
} else {
checkOverwrite()
}
})
}
}))
}
module.exports = function packager (opts, cb) {
var archs = validateList(opts.all ? 'all' : opts.arch, supportedArchs, 'arch')
var platforms = validateList(opts.all ? 'all' : opts.platform, supportedPlatforms, 'platform')
if (!opts.version) return cb(new Error('Must specify version'))
if (!Array.isArray(archs)) return cb(new Error(archs))
if (!Array.isArray(platforms)) return cb(new Error(platforms))
// Ignore this and related modules by default
var defaultIgnores = ['/node_modules/electron-prebuilt($|/)', '/node_modules/electron-packager($|/)', '/\.git($|/)']
if (opts.ignore && !Array.isArray(opts.ignore)) opts.ignore = [opts.ignore]
opts.ignore = (opts.ignore) ? opts.ignore.concat(defaultIgnores) : defaultIgnores
series(createSeries(opts, archs, platforms), function (err, appPaths) {
if (err) return cb(err)
cb(null, appPaths.filter(function (appPath) {
// Remove falsy entries (e.g. skipped platforms)
return appPath
}))
})
}

View File

@ -1,15 +0,0 @@
var path = require('path')
var mv = require('mv')
var common = require('./common')
module.exports = {
createApp: function createApp (opts, templatePath, callback) {
common.initializeApp(opts, templatePath, path.join('resources', 'app'), function buildLinuxApp (err, tempPath) {
if (err) return callback(err)
mv(path.join(tempPath, 'electron'), path.join(tempPath, opts.name), function (err) {
if (err) return callback(err)
common.moveApp(opts, tempPath, callback)
})
})
}
}

87
mac.js
View File

@ -1,87 +0,0 @@
var path = require('path')
var fs = require('fs')
var child = require('child_process')
var plist = require('plist')
var mv = require('mv')
var ncp = require('ncp').ncp
var series = require('run-series')
var common = require('./common')
module.exports = {
createApp: function createApp (opts, templatePath, callback) {
var appRelativePath = path.join('Electron.app', 'Contents', 'Resources', 'app')
common.initializeApp(opts, templatePath, appRelativePath, function buildMacApp (err, tempPath) {
if (err) return callback(err)
var contentsPath = path.join(tempPath, 'Electron.app', 'Contents')
var helperPath = path.join(contentsPath, 'Frameworks', 'Electron Helper.app')
var appPlistFilename = path.join(contentsPath, 'Info.plist')
var helperPlistFilename = path.join(helperPath, 'Contents', 'Info.plist')
var appPlist = plist.parse(fs.readFileSync(appPlistFilename).toString())
var helperPlist = plist.parse(fs.readFileSync(helperPlistFilename).toString())
// Update plist files
var defaultBundleName = 'com.electron.' + opts.name.toLowerCase().replace(/ /g, '_')
var appVersion = opts['app-version']
appPlist.CFBundleDisplayName = opts.name
appPlist.CFBundleIdentifier = opts['app-bundle-id'] || defaultBundleName
appPlist.CFBundleName = opts.name
helperPlist.CFBundleIdentifier = opts['helper-bundle-id'] || defaultBundleName + '.helper'
helperPlist.CFBundleName = opts.name
if (appVersion) {
appPlist.CFBundleVersion = appVersion
}
if (opts.protocols) {
helperPlist.CFBundleURLTypes = appPlist.CFBundleURLTypes = opts.protocols.map(function (protocol) {
return {
CFBundleURLName: protocol.name,
CFBundleURLSchemes: [].concat(protocol.schemes)
}
})
}
fs.writeFileSync(appPlistFilename, plist.build(appPlist))
fs.writeFileSync(helperPlistFilename, plist.build(helperPlist))
var operations = []
if (opts.icon) {
operations.push(function (cb) {
common.normalizeExt(opts.icon, '.icns', function (err, icon) {
if (err) {
// Ignore error if icon doesn't exist, in case it's only available for other OS
cb(null)
} else {
ncp(icon, path.join(contentsPath, 'Resources', 'atom.icns'), cb)
}
})
})
}
// Move Helper binary, then Helper.app, then top-level .app
var finalAppPath = path.join(tempPath, opts.name + '.app')
operations.push(function (cb) {
var helperBinaryPath = path.join(helperPath, 'Contents', 'MacOS')
mv(path.join(helperBinaryPath, 'Electron Helper'), path.join(helperBinaryPath, opts.name + ' Helper'), cb)
}, function (cb) {
mv(helperPath, path.join(path.dirname(helperPath), opts.name + ' Helper.app'), cb)
}, function (cb) {
mv(path.dirname(contentsPath), finalAppPath, cb)
})
if (opts.sign) {
operations.push(function (cb) {
child.exec('codesign --deep --force --sign "' + opts.sign + '" ' + finalAppPath, cb)
})
}
series(operations, function () {
common.moveApp(opts, tempPath, callback)
})
})
}
}

View File

@ -1,56 +1,37 @@
{
"name": "nativefier",
"version": "5.1.1",
"description": "Wrap single-page web apps natively",
"version": "6.0.0",
"description": "Wrap web apps natively",
"main": "index.js",
"bin": {
"nativefier": "cli.js"
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "babel src -d lib",
"watch": "babel --watch src -d lib"
},
"repository": {
"type": "git",
"url": "git+https://github.com/skewedlines/Nativefier.git"
"url": "git+https://github.com/jiahaog/nativefier.git"
},
"author": "Jia Hao",
"license": "BSD-2-Clause",
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/skewedlines/Nativefier/issues"
"url": "https://github.com/jiahaog/nativefier/issues"
},
"homepage": "https://github.com/skewedlines/Nativefier",
"homepage": "https://github.com/jiahaog/nativefier#readme",
"dependencies": {
"asar": "^0.6.1",
"electron-download": "^1.0.0",
"extract-zip": "^1.0.3",
"minimist": "^1.1.1",
"mkdirp": "^0.5.0",
"mv": "^2.0.3",
"async": "^1.5.2",
"commander": "^2.9.0",
"electron-packager": "^5.2.1",
"ncp": "^2.0.0",
"plist": "^1.1.0",
"rcedit": "^0.3.0",
"rimraf": "^2.3.2",
"run-series": "^1.1.1",
"temp": "^0.8.3",
"validator": "^3.40.1"
"tmp": "0.0.28"
},
"devDependencies": {
"run-waterfall": "^1.1.1",
"standard": "^3.3.2",
"tape": "^4.0.0"
},
"scripts": {
"test": "standard && tape test"
},
"standard": {
"ignore": [
"test/fixtures/**/node_modules"
"babel": {
"presets": [
"es2015"
]
},
"directories": {
"test": "test"
},
"keywords": [
"native",
"electron",
"package",
"cli"
]
"devDependencies": {
"babel-cli": "^6.4.0",
"babel-preset-es2015": "^6.3.13"
}
}

161
src/cli.js Normal file
View File

@ -0,0 +1,161 @@
import os from 'os';
import fs from 'fs';
import path from 'path';
import packager from 'electron-packager';
import commander from 'commander';
import tmp from 'tmp';
import ncp from 'ncp';
import async from 'async';
const copy = ncp.ncp;
const TEMPLATE_APP_DIR = path.join(__dirname, '../', 'app');
const ELECTRON_VERSION = '0.36.4';
function optionsFactory(name = 'MyApp',
targetUrl = 'http://google.com',
platform = detectPlatform(),
architecture = detectArch(),
version = ELECTRON_VERSION,
outDir = os.homedir(),
overwrite = true,
conceal = true,
iconDir,
badge = false,
width = 1280,
height = 800) {
return {
dir: TEMPLATE_APP_DIR,
name: name,
targetUrl: targetUrl,
platform: platform,
arch: architecture,
version: version,
out: outDir,
// optionals
overwrite: overwrite,
asar: conceal,
icon: iconDir,
// app configuration
badge: badge,
width: width,
height: height
}
}
/**
* @callback buildAppCallback
* @param error
* @param appPath
*/
/**
*
* @param options
* @param {buildAppCallback} callback
*/
function buildApp(options, callback) {
// pre process app
var tmpobj = tmp.dirSync({unsafeCleanup: true});
const tmpPath = tmpobj.name;
async.waterfall([
function (callback) {
console.log("Dir: ", tmpobj.name);
copyPlaceholderApp(options.dir, tmpPath, options.name, options.targetUrl, options.badge, options.width, options.height, callback);
},
function (tempDir, callback) {
console.log('copied to ', tempDir);
options.dir = tempDir;
packager(options, callback);
},
function (appPath, callback) {
tmpobj.removeCallback();
callback(null, appPath);
}
], callback);
}
function detectPlatform() {
const platform = os.platform();
if (platform === 'darwin' || platform === 'win32' || platform === 'linux') {
return platform;
}
console.warn(`Warning: Untested platform ${platform} detected, assuming linux`);
return 'linux';
}
function detectArch() {
const arch = os.arch();
if (arch !== 'ia32' && arch !== 'x64') {
throw `Incompatible architecture ${arch} detected`;
}
return os.arch();
}
function main() {
const options = optionsFactory();
buildApp(options, (error, appPath) => {
if (error) {
console.trace(error);
return;
}
console.log(`App built to ${appPath}`);
});
}
/**
* @callback tempDirCallback
* @param error
* @param [tempDirPath]
*/
/**
* Creates a temporary directory and copies the './app folder' inside, and adds a text file with the configuration
* for the single page app.
*
* @param {string} srcAppDir
* @param {string} tempDir
* @param {string} name
* @param {string} targetURL
* @param {boolean} badge
* @param {number} [width]
* @param {number} [height]
* @param {tempDirCallback} callback
*/
function copyPlaceholderApp(srcAppDir, tempDir, name, targetURL, badge, width, height, callback) {
copy(srcAppDir, tempDir, function (error) {
if (error) {
console.error(error);
callback(`Error Copying temporary directory: ${error}`);
return;
}
const appArgs = {
name: name,
targetUrl: targetURL,
badge: badge,
width: width,
height: height
};
fs.writeFileSync(path.join(tempDir, '/targetUrl.txt'), JSON.stringify(appArgs));
callback(null, tempDir);
});
};
main();

View File

@ -1,51 +0,0 @@
/**
* Created by JiaHao on 5/7/15.
*/
var fs = require('fs');
var temp = require('temp').track();
var ncp = require('ncp').ncp;
/**
* @callback tempDirCallback
* @param error
* @param tempDirPath
*/
/**
* Creates a temporary directory and copies the './app folder' inside, and adds a text file with the configuration
* for the single page app.
*
* @param {string} name
* @param {string} targetURL
* @param {boolean} badge
* @param width
* @param height
* @param {tempDirCallback} callback
*/
module.exports = function (name, targetURL, badge, width, height, callback) {
var tempDir = temp.path();
ncp(__dirname + '/app', tempDir, function (error) {
if (error) {
console.error(error);
callback('Error Copying temporary directory\n' + error, null);
} else {
var appArgs = {
name: name,
targetUrl: targetURL,
badge: badge,
width: width,
height: height
};
fs.writeFileSync(tempDir + '/targetUrl.txt', JSON.stringify(appArgs));
callback(error, tempDir);
}
});
};

View File

@ -1,273 +0,0 @@
var fs = require('fs')
var path = require('path')
var packager = require('..')
var waterfall = require('run-waterfall')
var config = require('./config.json')
var util = require('./util')
function generateBasename (opts) {
return opts.name + '-' + opts.platform + '-' + opts.arch
}
function generateNamePath (opts) {
// Generates path to verify reflects the name given in the options.
// Returns the Helper.app location on darwin since the top-level .app is already tested for the resources path;
// returns the executable for other OSes
if (opts.platform === 'darwin') {
return path.join(opts.name + '.app', 'Contents', 'Frameworks', opts.name + ' Helper.app')
}
return opts.name + (opts.platform === 'win32' ? '.exe' : '')
}
function createDefaultsTest (combination) {
return function (t) {
t.timeoutAfter(config.timeout)
var opts = Object.create(combination)
opts.name = 'basicTest'
opts.dir = path.join(__dirname, 'fixtures', 'basic')
var finalPath
var resourcesPath
waterfall([
function (cb) {
packager(opts, cb)
}, function (paths, cb) {
t.true(Array.isArray(paths), 'packager call should resolve to an array')
t.equal(paths.length, 1, 'Single-target run should resolve to a 1-item array')
finalPath = paths[0]
t.equal(finalPath, path.join(util.getWorkCwd(), generateBasename(opts)),
'Path should follow the expected format and be in the cwd')
fs.stat(finalPath, cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The expected output directory should exist')
resourcesPath = path.join(finalPath, util.generateResourcesPath(opts))
fs.stat(path.join(finalPath, generateNamePath(opts)), cb)
}, function (stats, cb) {
if (opts.platform === 'darwin') {
t.true(stats.isDirectory(), 'The Helper.app should reflect opts.name')
} else {
t.true(stats.isFile(), 'The executable should reflect opts.name')
}
fs.stat(resourcesPath, cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory')
fs.stat(path.join(resourcesPath, 'app', 'node_modules', 'run-waterfall'), cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The output directory should contain devDependencies by default (no prune)')
util.areFilesEqual(path.join(opts.dir, 'main.js'), path.join(resourcesPath, 'app', 'main.js'), cb)
}, function (equal, cb) {
t.true(equal, 'File under packaged app directory should match source file')
util.areFilesEqual(path.join(opts.dir, 'ignore', 'this.txt'),
path.join(resourcesPath, 'app', 'ignore', 'this.txt'),
cb)
}, function (equal, cb) {
t.true(equal,
'File under subdirectory of packaged app directory should match source file and not be ignored by default')
cb()
}
], function (err) {
t.end(err)
})
}
}
function createOutTest (combination) {
return function (t) {
t.timeoutAfter(config.timeout)
var opts = Object.create(combination)
opts.name = 'basicTest'
opts.dir = path.join(__dirname, 'fixtures', 'basic')
opts.out = 'dist'
var finalPath
waterfall([
function (cb) {
packager(opts, cb)
}, function (paths, cb) {
finalPath = paths[0]
t.equal(finalPath, path.join('dist', generateBasename(opts)),
'Path should follow the expected format and be under the folder specifed in `out`')
fs.stat(finalPath, cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The expected output directory should exist')
fs.stat(path.join(finalPath, util.generateResourcesPath(opts)), cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory')
cb()
}
], function (err) {
t.end(err)
})
}
}
function createAsarTest (combination) {
return function (t) {
t.timeoutAfter(config.timeout)
var opts = Object.create(combination)
opts.name = 'basicTest'
opts.dir = path.join(__dirname, 'fixtures', 'basic')
opts.asar = true
var finalPath
var resourcesPath
waterfall([
function (cb) {
packager(opts, cb)
}, function (paths, cb) {
finalPath = paths[0]
fs.stat(finalPath, cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The expected output directory should exist')
resourcesPath = path.join(finalPath, util.generateResourcesPath(opts))
fs.stat(resourcesPath, cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory')
fs.stat(path.join(resourcesPath, 'app.asar'), cb)
}, function (stats, cb) {
t.true(stats.isFile(), 'app.asar should exist under the resources subdirectory when opts.asar is true')
fs.exists(path.join(resourcesPath, 'app'), function (exists) {
t.false(exists, 'app subdirectory should NOT exist when app.asar is built')
cb()
})
}
], function (err) {
t.end(err)
})
}
}
function createPruneTest (combination) {
return function (t) {
t.timeoutAfter(config.timeout)
var opts = Object.create(combination)
opts.name = 'basicTest'
opts.dir = path.join(__dirname, 'fixtures', 'basic')
opts.prune = true
var finalPath
var resourcesPath
waterfall([
function (cb) {
packager(opts, cb)
}, function (paths, cb) {
finalPath = paths[0]
fs.stat(finalPath, cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The expected output directory should exist')
resourcesPath = path.join(finalPath, util.generateResourcesPath(opts))
fs.stat(resourcesPath, cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory')
fs.stat(path.join(resourcesPath, 'app', 'node_modules', 'run-series'), cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'npm dependency should exist under app/node_modules')
fs.exists(path.join(resourcesPath, 'app', 'node_modules', 'run-waterfall'), function (exists) {
t.false(exists, 'npm devDependency should NOT exist under app/node_modules')
cb()
})
}
], function (err) {
t.end(err)
})
}
}
function createIgnoreTest (combination, ignorePattern, ignoredFile) {
return function (t) {
t.timeoutAfter(config.timeout)
var opts = Object.create(combination)
opts.name = 'basicTest'
opts.dir = path.join(__dirname, 'fixtures', 'basic')
opts.ignore = ignorePattern
var appPath
waterfall([
function (cb) {
packager(opts, cb)
}, function (paths, cb) {
appPath = path.join(paths[0], util.generateResourcesPath(opts), 'app')
fs.stat(path.join(appPath, 'package.json'), cb)
}, function (stats, cb) {
t.true(stats.isFile(), 'The expected output directory should exist and contain files')
fs.exists(path.join(appPath, ignoredFile), function (exists) {
t.false(exists, 'Ignored file should not exist in output app directory')
cb()
})
}
], function (err) {
t.end(err)
})
}
}
function createOverwriteTest (combination) {
return function (t) {
t.timeoutAfter(config.timeout * 2) // Multiplied since this test packages the application twice
var opts = Object.create(combination)
opts.name = 'basicTest'
opts.dir = path.join(__dirname, 'fixtures', 'basic')
var finalPath
var testPath
waterfall([
function (cb) {
packager(opts, cb)
}, function (paths, cb) {
finalPath = paths[0]
fs.stat(finalPath, cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The expected output directory should exist')
// Create a dummy file to detect whether the output directory is replaced in subsequent runs
testPath = path.join(finalPath, 'test.txt')
fs.writeFile(testPath, 'test', cb)
}, function (cb) {
// Run again, defaulting to overwrite false
packager(opts, cb)
}, function (paths, cb) {
fs.stat(testPath, cb)
}, function (stats, cb) {
t.true(stats.isFile(), 'The existing output directory should exist as before (skipped by default)')
// Run a third time, explicitly setting overwrite to true
opts.overwrite = true
packager(opts, cb)
}, function (paths, cb) {
fs.exists(testPath, function (exists) {
t.false(exists, 'The output directory should be regenerated when overwrite is true')
cb()
})
}
], function (err) {
t.end(err)
})
}
}
util.testAllPlatforms('defaults test', createDefaultsTest)
util.testAllPlatforms('out test', createOutTest)
util.testAllPlatforms('asar test', createAsarTest)
util.testAllPlatforms('prune test', createPruneTest)
util.testAllPlatforms('ignore test: string in array', createIgnoreTest, ['ignorethis'], 'ignorethis.txt')
util.testAllPlatforms('ignore test: string', createIgnoreTest, 'ignorethis', 'ignorethis.txt')
util.testAllPlatforms('ignore test: RegExp', createIgnoreTest, /ignorethis/, 'ignorethis.txt')
util.testAllPlatforms('ignore test: string with slash', createIgnoreTest, 'ignore/this',
path.join('ignore', 'this.txt'))
util.testAllPlatforms('ignore test: only match subfolder of app', createIgnoreTest, 'electron-packager',
path.join('electron-packager', 'readme.txt'))
util.testAllPlatforms('overwrite test', createOverwriteTest)

View File

@ -1,4 +0,0 @@
{
"timeout": 15000,
"version": "0.28.3"
}

View File

@ -1,2 +0,0 @@
This file exists to test ability to ignore paths under app, without also
ignoring the entire app folder due to a match above it (#54 / #55).

View File

View File

@ -1 +0,0 @@

View File

@ -1,4 +0,0 @@
<!DOCTYPE html>
<html>
<body>Hello, world!</body>
</html>

View File

@ -1,22 +0,0 @@
var app = require('app')
var BrowserWindow = require('browser-window')
var mainWindow
app.on('window-all-closed', function () {
app.quit()
})
app.on('ready', function () {
mainWindow = new BrowserWindow({
center: true,
title: 'Basic Test',
width: 800,
height: 600
})
mainWindow.loadUrl('file://' + require('path').resolve(__dirname, 'index.html'))
mainWindow.on('closed', function () {
mainWindow = null
})
})

View File

@ -1,9 +0,0 @@
{
"main": "main.js",
"dependencies": {
"run-series": "^1.1.1"
},
"devDependencies": {
"run-waterfall": "^1.1.1"
}
}

Binary file not shown.

View File

@ -1,27 +0,0 @@
var exec = require('child_process').exec
var path = require('path')
var series = require('run-series')
var config = require('./config.json')
var util = require('./util')
// Download all Electron distributions before running tests to avoid timing out due to network speed
series([
function (cb) {
console.log('Calling electron-download before running tests...')
util.downloadAll(config.version, cb)
}, function (cb) {
console.log('Running npm install in fixtures/basic...')
exec('npm install', {cwd: path.join(__dirname, 'fixtures', 'basic')}, cb)
}
], function () {
console.log('Running tests...')
require('./basic')
require('./multitarget')
if (process.platform !== 'win32') {
// Perform additional tests specific to building for OS X
require('./mac')
}
})

View File

@ -1,94 +0,0 @@
var exec = require('child_process').exec
var fs = require('fs')
var path = require('path')
var packager = require('..')
var test = require('tape')
var waterfall = require('run-waterfall')
var config = require('./config.json')
var util = require('./util')
function createIconTest (icon, iconPath) {
return function (t) {
t.timeoutAfter(config.timeout)
var opts = {
name: 'basicTest',
dir: path.join(__dirname, 'fixtures', 'basic'),
version: config.version,
arch: 'x64',
platform: 'darwin',
icon: icon
}
var resourcesPath
waterfall([
function (cb) {
packager(opts, cb)
}, function (paths, cb) {
resourcesPath = path.join(paths[0], util.generateResourcesPath(opts))
fs.stat(resourcesPath, cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The output directory should contain the expected resources subdirectory')
util.areFilesEqual(iconPath, path.join(resourcesPath, 'atom.icns'), cb)
}, function (equal, cb) {
t.true(equal, 'atom.icns should be identical to the specified icon file')
cb()
}
], function (err) {
t.end(err)
})
}
}
var iconBase = path.join(__dirname, 'fixtures', 'monochrome')
var icnsPath = iconBase + '.icns'
util.setup()
test('icon test: .icns specified', createIconTest(icnsPath, icnsPath))
util.teardown()
util.setup()
test('icon test: .ico specified (should replace with .icns)', createIconTest(iconBase + '.ico', icnsPath))
util.teardown()
util.setup()
test('icon test: basename only (should add .icns)', createIconTest(iconBase, icnsPath))
util.teardown()
util.setup()
test('codesign test', function (t) {
t.timeoutAfter(config.timeout)
var opts = {
name: 'basicTest',
dir: path.join(__dirname, 'fixtures', 'basic'),
version: config.version,
arch: 'x64',
platform: 'darwin',
sign: '-' // Ad-hoc
}
var appPath
waterfall([
function (cb) {
packager(opts, cb)
}, function (paths, cb) {
appPath = path.join(paths[0], opts.name + '.app')
fs.stat(appPath, cb)
}, function (stats, cb) {
t.true(stats.isDirectory(), 'The expected .app directory should exist')
exec('codesign --verify --deep ' + appPath, cb)
}, function (stdout, stderr, cb) {
t.pass('codesign should verify successfully')
cb()
}
], function (err) {
var notFound = err && err.code === 127
if (notFound) console.log('codesign not installed; skipped')
t.end(notFound ? null : err)
})
})
util.teardown()

View File

@ -1,145 +0,0 @@
var fs = require('fs')
var path = require('path')
var packager = require('..')
var series = require('run-series')
var test = require('tape')
var waterfall = require('run-waterfall')
var config = require('./config.json')
var util = require('./util')
function verifyPackageExistence (finalPaths, callback) {
series(finalPaths.map(function (finalPath) {
return function (cb) {
fs.stat(finalPath, cb)
}
}), function (err, statsResults) {
if (err) return callback(null, false)
callback(null, statsResults.every(function (stats) {
return stats.isDirectory()
}))
})
}
util.setup()
test('all test', function (t) {
t.timeoutAfter(config.timeout * 5) // 4-5 packages will be built during this test
var opts = {
name: 'basicTest',
dir: path.join(__dirname, 'fixtures', 'basic'),
version: config.version,
all: true
}
waterfall([
function (cb) {
packager(opts, cb)
}, function (finalPaths, cb) {
// Windows skips packaging for OS X, and OS X only has 64-bit releases
t.equal(finalPaths.length, process.platform === 'win32' ? 4 : 5,
'packager call should resolve with expected number of paths')
verifyPackageExistence(finalPaths, cb)
}, function (exists, cb) {
t.true(exists, 'Packages should be generated for all possible platforms')
cb()
}
], function (err) {
t.end(err)
})
})
util.teardown()
util.setup()
test('platform=all test (one arch)', function (t) {
t.timeoutAfter(config.timeout * 2) // 2 packages will be built during this test
var opts = {
name: 'basicTest',
dir: path.join(__dirname, 'fixtures', 'basic'),
version: config.version,
arch: 'ia32',
platform: 'all'
}
waterfall([
function (cb) {
packager(opts, cb)
}, function (finalPaths, cb) {
t.equal(finalPaths.length, 2, 'packager call should resolve with expected number of paths')
verifyPackageExistence(finalPaths, cb)
}, function (exists, cb) {
t.true(exists, 'Packages should be generated for both 32-bit platforms')
cb()
}
], function (err) {
t.end(err)
})
})
util.teardown()
util.setup()
test('arch=all test (one platform)', function (t) {
t.timeoutAfter(config.timeout * 2) // 2 packages will be built during this test
var opts = {
name: 'basicTest',
dir: path.join(__dirname, 'fixtures', 'basic'),
version: config.version,
arch: 'all',
platform: 'linux'
}
waterfall([
function (cb) {
packager(opts, cb)
}, function (finalPaths, cb) {
t.equal(finalPaths.length, 2, 'packager call should resolve with expected number of paths')
verifyPackageExistence(finalPaths, cb)
}, function (exists, cb) {
t.true(exists, 'Packages should be generated for both architectures')
cb()
}
], function (err) {
t.end(err)
})
})
util.teardown()
function createMultiTest (arch, platform) {
return function (t) {
t.timeoutAfter(config.timeout * 4) // 4 packages will be built during this test
var opts = {
name: 'basicTest',
dir: path.join(__dirname, 'fixtures', 'basic'),
version: config.version,
arch: arch,
platform: platform
}
waterfall([
function (cb) {
packager(opts, cb)
}, function (finalPaths, cb) {
t.equal(finalPaths.length, 4, 'packager call should resolve with expected number of paths')
verifyPackageExistence(finalPaths, cb)
}, function (exists, cb) {
t.true(exists, 'Packages should be generated for all combinations of specified archs and platforms')
cb()
}
], function (err) {
t.end(err)
})
}
}
util.setup()
test('multi-platform / multi-arch test, from arrays', createMultiTest(['ia32', 'x64'], ['linux', 'win32']))
util.teardown()
util.setup()
test('multi-platform / multi-arch test, from strings', createMultiTest('ia32,x64', 'linux,win32'))
util.teardown()

View File

@ -1,100 +0,0 @@
var fs = require('fs')
var path = require('path')
var test = require('tape')
var download = require('electron-download')
var mkdirp = require('mkdirp')
var rimraf = require('rimraf')
var series = require('run-series')
var ORIGINAL_CWD = process.cwd()
var WORK_CWD = path.join(__dirname, 'work')
var archs = ['ia32', 'x64']
var platforms = ['darwin', 'linux', 'win32']
var slice = Array.prototype.slice
var version = require('./config.json').version
var combinations = []
archs.forEach(function (arch) {
platforms.forEach(function (platform) {
// Electron does not have 32-bit releases for Mac OS X, so skip that combination
// Also skip testing darwin target on Windows since electron-packager itself skips it
// (see https://github.com/maxogden/electron-packager/issues/71)
if (platform === 'darwin' && (arch === 'ia32' || require('os').platform() === 'win32')) return
combinations.push({
arch: arch,
platform: platform,
version: version
})
})
})
exports.areFilesEqual = function areFilesEqual (file1, file2, callback) {
series([
function (cb) {
fs.readFile(file1, cb)
},
function (cb) {
fs.readFile(file2, cb)
}
], function (err, buffers) {
callback(err, slice.call(buffers[0]).every(function (b, i) {
return b === buffers[1][i]
}))
})
}
exports.downloadAll = function downloadAll (version, callback) {
series(combinations.map(function (combination) {
return function (cb) {
download(combination, cb)
}
}), callback)
}
exports.forEachCombination = function forEachCombination (cb) {
combinations.forEach(cb)
}
exports.generateResourcesPath = function generateResourcesPath (opts) {
return opts.platform === 'darwin' ? path.join(opts.name + '.app', 'Contents', 'Resources') : 'resources'
}
exports.getWorkCwd = function getWorkCwd () {
return WORK_CWD
}
// tape doesn't seem to have a provision for before/beforeEach/afterEach/after,
// so run setup/teardown and cleanup tasks as additional "tests" to put them in sequence
// and run them irrespective of test failures
exports.setup = function setup () {
test('setup', function (t) {
mkdirp(WORK_CWD, function (err) {
if (err) t.end(err)
process.chdir(WORK_CWD)
t.end()
})
})
}
exports.teardown = function teardown () {
test('teardown', function (t) {
process.chdir(ORIGINAL_CWD)
rimraf(WORK_CWD, function (err) {
t.end(err)
})
})
}
exports.testAllPlatforms = function testAllPlatforms (name, createTest /*, ...createTestArgs */) {
var args = slice.call(arguments, 2)
exports.setup()
exports.forEachCombination(function (combination) {
test(name + ': ' + combination.platform + '-' + combination.arch,
createTest.apply(null, [combination].concat(args)))
})
exports.teardown()
}

View File

@ -1,40 +0,0 @@
Usage: nativefier <appname> <target> --platform=<platform> --arch=<arch> --version=<version>
Required options
appname name for the app
target target url for the single page app
platform all, or one or more of: linux, win32, darwin (comma-delimited if multiple)
arch all, ia32, x64
version see https://github.com/atom/electron/releases
Example nativefier Messenger "http://messenger.com" --platform=darwin --arch=x64 --version=0.28.2
Optional options
all equivalent to --platform=all --arch=all
out the dir to put the app into at the end. defaults to current working dir
icon the icon file to use as the icon for the app (should be a .icns file on OSX)
app-bundle-id bundle identifier to use in the app plist
app-version version to set for the app
helper-bundle-id bundle identifier to use in the app helper plist
ignore do not copy files into App whose filenames regex .match this string
prune runs `npm prune --production` on the app
overwrite if output directory for a platform already exists, replaces it rather than skipping it
asar packages the source code within your app into an archive
sign should contain the identity to be used when running `codesign` (OS X only)
version-string should contain a hash of the application metadata to be embedded into the executable (Windows only).
These can be specified on the command line via dot notation,
e.g. --version-string.CompanyName="Company Inc." --version-string.ProductName="Product"
Keys supported:
- CompanyName
- LegalCopyright
- FileDescription
- OriginalFilename
- FileVersion
- ProductVersion
- ProductName
- InternalName
badge if the target app should show badges in the OSX dock on receipt of desktop notifications
width window width (default=1280)
height window height (default=800)

View File

@ -1,40 +0,0 @@
var path = require('path')
var mv = require('mv')
var series = require('run-series')
var common = require('./common')
module.exports = {
createApp: function createApp (opts, templatePath, callback) {
common.initializeApp(opts, templatePath, path.join('resources', 'app'), function buildWinApp (err, tempPath) {
if (err) return callback(err)
var newExePath = path.join(tempPath, opts.name + '.exe')
var operations = [
function (cb) {
mv(path.join(tempPath, 'electron.exe'), newExePath, cb)
}
]
if (opts.icon || opts['version-string']) {
operations.push(function (cb) {
common.normalizeExt(opts.icon, '.ico', function (err, icon) {
var rcOpts = {}
if (opts['version-string']) rcOpts['version-string'] = opts['version-string']
// Icon might be omitted or only exist in one OS's format, so skip it if normalizeExt reports an error
if (!err) {
rcOpts.icon = icon
}
require('rcedit')(newExePath, rcOpts, cb)
})
})
}
series(operations, function () {
common.moveApp(opts, tempPath, callback)
})
})
}
}