mirror of
https://github.com/Llewellynvdm/nativefier.git
synced 2025-01-03 06:10:20 +00:00
Initial commit for working module only with api change
This commit is contained in:
parent
5f20dca5cc
commit
5379740dda
25
.editorconfig
Normal file
25
.editorconfig
Normal 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
83
.gitignore
vendored
@ -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
|
||||
|
@ -1,3 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '0.12'
|
@ -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
23
LICENSE
@ -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
55
cli.js
@ -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]);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
@ -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
139
common.js
@ -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
169
index.js
@ -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
|
||||
}))
|
||||
})
|
||||
}
|
15
linux.js
15
linux.js
@ -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
87
mac.js
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
63
package.json
63
package.json
@ -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
161
src/cli.js
Normal 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();
|
51
tempDir.js
51
tempDir.js
@ -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);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
273
test/basic.js
273
test/basic.js
@ -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)
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"timeout": 15000,
|
||||
"version": "0.28.3"
|
||||
}
|
@ -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).
|
0
test/fixtures/basic/ignore/this.txt
vendored
0
test/fixtures/basic/ignore/this.txt
vendored
1
test/fixtures/basic/ignorethis.txt
vendored
1
test/fixtures/basic/ignorethis.txt
vendored
@ -1 +0,0 @@
|
||||
|
4
test/fixtures/basic/index.html
vendored
4
test/fixtures/basic/index.html
vendored
@ -1,4 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>Hello, world!</body>
|
||||
</html>
|
22
test/fixtures/basic/main.js
vendored
22
test/fixtures/basic/main.js
vendored
@ -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
|
||||
})
|
||||
})
|
9
test/fixtures/basic/package.json
vendored
9
test/fixtures/basic/package.json
vendored
@ -1,9 +0,0 @@
|
||||
{
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
"run-series": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"run-waterfall": "^1.1.1"
|
||||
}
|
||||
}
|
BIN
test/fixtures/monochrome.icns
vendored
BIN
test/fixtures/monochrome.icns
vendored
Binary file not shown.
@ -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')
|
||||
}
|
||||
})
|
94
test/mac.js
94
test/mac.js
@ -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()
|
@ -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()
|
100
test/util.js
100
test/util.js
@ -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()
|
||||
}
|
40
usage.txt
40
usage.txt
@ -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)
|
40
win32.js
40
win32.js
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user