Skip to content

Commit

Permalink
feat: add Node 8 syntax support (#27)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Drops support for Node < 8.

* Adjust `wrapError` to be more `async`/`await` - friendly
* refactor: convert tests to async/await syntax
* chore: replace glob-promise with util.promisify
* chore: upgrade asar to ^2.0.1
* chore: upgrade eslint-plugin-node to ^9.0.1
* chore: upgrade tmp-promise to ^2.0.1
  • Loading branch information
malept authored May 23, 2019
1 parent b57ca6e commit dfcab00
Show file tree
Hide file tree
Showing 19 changed files with 240 additions and 231 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: node_js
node_js: '6'
node_js: '8'
os:
- linux
- osx
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

## Requirements

Requires Node 6 or greater.
Requires Node 8 or greater.

## Legal

Expand Down
16 changes: 7 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,30 @@
],
"devDependencies": {
"ava": "^1.0.1",
"codecov": "^3.1.0",
"codecov": "^3.5.0",
"eslint": "^5.10.0",
"eslint-config-standard": "^12.0.0",
"eslint-plugin-ava": "^6.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^8.0.0",
"eslint-plugin-node": "^9.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"nyc": "^14.0.0",
"nyc": "^14.1.1",
"sinon": "^7.2.2"
},
"dependencies": {
"asar": "^1.0.0",
"asar": "^2.0.1",
"cross-spawn-promise": "^0.10.1",
"debug": "^4.1.1",
"fs-extra": "^8.0.1",
"glob": "^7.1.3",
"glob-promise": "^3.4.0",
"glob": "^7.1.4",
"lodash": "^4.17.11",
"parse-author": "^2.0.0",
"semver": "^6.0.0",
"tmp-promise": "^1.0.5"
"tmp-promise": "^2.0.1"
},
"engines": {
"node": ">= 6.0"
"node": ">= 8.0"
},
"ava": {
"babel": false,
Expand All @@ -67,7 +66,6 @@
"ava"
],
"rules": {
"ava/prefer-async-await": 0,
"node/no-unpublished-require": [
"error",
{
Expand Down
5 changes: 2 additions & 3 deletions src/desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ const path = require('path')
const { wrapError } = require('./error')

module.exports = {
createDesktopFile: function createDesktopFile (templatePath, dir, baseName, options) {
createDesktopFile: async function createDesktopFile (templatePath, dir, baseName, options) {
const dest = path.join(dir, `${baseName}.desktop`)
debug(`Creating desktop file at ${dest}`)

return createTemplatedFile(templatePath, dest, options, 0o644)
.catch(wrapError('creating desktop file'))
return wrapError('creating desktop file', () => createTemplatedFile(templatePath, dest, options, 0o644))
}
}
25 changes: 21 additions & 4 deletions src/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,28 @@ module.exports = {
* ```javascript
* Promise.reject(new Error('My error')).catch(wrapError('with the code'))
* ```
*
* The `wrappedFunction` parameter is used for async/await use cases. For example:
*
* ```javascript
* wrapError('with the code', async () => {
* await foo();
* await bar();
* })
* ```
*/
wrapError: function wrapError (message) {
return err => {
err.message = errorMessage(message, err)
throw err
wrapError: function wrapError (message, wrappedFunction) {
if (wrappedFunction) {
try {
return wrappedFunction()
} catch (error) {
module.exports.wrapError(message)(error)
}
} else {
return err => {
err.message = errorMessage(message, err)
throw err
}
}
}
}
107 changes: 54 additions & 53 deletions src/installer.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
'use strict'

const { promisify } = require('util')

const _ = require('lodash')
const debug = require('debug')('electron-installer-common:installer')
const desktop = require('./desktop')
const error = require('./error')
const fs = require('fs-extra')
const glob = require('glob-promise')
const glob = promisify(require('glob'))
const path = require('path')
const template = require('./template')
const tmp = require('tmp-promise')
const updateSandboxHelperPermissions = require('./sandboxhelper')

tmp.setGracefulCleanup()

class ElectronInstaller {
constructor (userSupplied) {
this.userSupplied = userSupplied
Expand Down Expand Up @@ -61,48 +65,45 @@ class ElectronInstaller {
/**
* Copies the bundled application into the staging directory.
*/
copyApplication (ignoreFunc) {
async copyApplication (ignoreFunc) {
debug(`Copying application to ${this.stagingAppDir}`)

return fs.ensureDir(this.stagingAppDir, '0755')
.then(() => fs.copy(this.sourceDir, this.stagingAppDir, { filter: ignoreFunc }))
.catch(error.wrapError('copying application directory'))
return error.wrapError('copying application directory', async () => {
await fs.ensureDir(this.stagingAppDir, '0755')
return fs.copy(this.sourceDir, this.stagingAppDir, { filter: ignoreFunc })
})
}

/**
* Create hicolor icon for the package.
*/
copyHicolorIcons () {
async copyHicolorIcons () {
return Promise.all(_.map(this.options.icon, (iconSrc, resolution) => {
const iconExt = resolution === 'scalable' ? 'svg' : 'png'
const iconFile = path.join(this.stagingDir, this.baseAppDir, 'share', 'icons', 'hicolor', resolution, 'apps', `${this.appIdentifier}.${iconExt}`)

return this.copyIcon(iconSrc, iconFile)
.catch(error.wrapError('creating hicolor icon file'))
return error.wrapError('creating hicolor icon file', async () => this.copyIcon(iconSrc, iconFile))
}))
}

/**
* Generically copy an icon.
*/
copyIcon (src, dest) {
async copyIcon (src, dest) {
debug(`Copying icon file at from "${src}" to "${dest}"`)

return fs.pathExists(src)
.then(exists => {
if (!exists) {
throw new Error(`The icon "${src}" does not exist`)
}
return true
}).then(() => fs.ensureDir(path.dirname(dest), '0755'))
.then(() => fs.copy(src, dest))
.then(() => fs.chmod(dest, '0644'))
if (!await fs.pathExists(src)) {
throw new Error(`The icon "${src}" does not exist`)
}
await fs.ensureDir(path.dirname(dest), '0755')
await fs.copy(src, dest)
return fs.chmod(dest, '0644')
}

/**
* Copy `LICENSE` from the root of the app to a different location.
*/
copyLicense (copyrightFile) {
async copyLicense (copyrightFile) {
const licenseSrc = path.join(this.sourceDir, 'LICENSE')
debug(`Copying license file from ${licenseSrc}`)

Expand All @@ -112,7 +113,7 @@ class ElectronInstaller {
/**
* Copy icons into the appropriate locations on Linux.
*/
copyLinuxIcons () {
async copyLinuxIcons () {
if (_.isObject(this.options.icon)) {
return this.copyHicolorIcons()
} else if (this.options.icon) {
Expand All @@ -123,63 +124,61 @@ class ElectronInstaller {
/**
* Create pixmap icon for the package.
*/
copyPixmapIcon () {
async copyPixmapIcon () {
const iconFile = path.join(this.stagingDir, this.baseAppDir, this.pixmapIconPath)

return this.copyIcon(this.options.icon, iconFile)
.catch(error.wrapError('creating pixmap icon file'))
return error.wrapError('creating pixmap icon file', async () => this.copyIcon(this.options.icon, iconFile))
}

/**
* Create the symlink to the binary for the package.
*/
createBinarySymlink () {
async createBinarySymlink () {
const binSrc = path.join('../lib', this.appIdentifier, this.options.bin)
const binDest = path.join(this.stagingDir, this.baseAppDir, 'bin', this.appIdentifier)
debug(`Symlinking binary from ${binSrc} to ${binDest}`)

const bundledBin = path.join(this.sourceDir, this.options.bin)

return fs.pathExists(bundledBin)
.then(exists => {
if (!exists) {
throw new Error(`could not find the Electron app binary at "${bundledBin}". You may need to re-bundle the app using Electron Packager's "executableName" option.`)
}
return fs.ensureDir(path.dirname(binDest), '0755')
}).then(() => fs.symlink(binSrc, binDest, 'file'))
.catch(error.wrapError('creating binary symlink'))
return error.wrapError('creating binary symlink', async () => {
if (!await fs.pathExists(bundledBin)) {
throw new Error(`could not find the Electron app binary at "${bundledBin}". You may need to re-bundle the app using Electron Packager's "executableName" option.`)
}
await fs.ensureDir(path.dirname(binDest), '0755')
return fs.symlink(binSrc, binDest, 'file')
})
}

/**
* Generate the contents of the package in "parallel" by calling the methods specified in
* `contentFunctions` getter through `Promise.all`.
*/
createContents () {
async createContents () {
debug('Creating contents of package')

return Promise.all(this.contentFunctions.map(func => this[func]()))
.catch(error.wrapError('creating contents of package'))
return error.wrapError('creating contents of package', async () => Promise.all(this.contentFunctions.map(func => this[func]())))
}

/**
* Create copyright for the package.
*/
createCopyright () {
async createCopyright () {
const copyrightFile = path.join(this.stagingDir, this.baseAppDir, 'share', 'doc', this.appIdentifier, 'copyright')
debug(`Creating copyright file at ${copyrightFile}`)

return fs.ensureDir(path.dirname(copyrightFile), '0755')
.then(() => this.copyLicense(copyrightFile))
.then(() => fs.chmod(copyrightFile, '0644'))
.catch(error.wrapError('creating copyright file'))
return error.wrapError('creating copyright file', async () => {
await fs.ensureDir(path.dirname(copyrightFile), '0755')
await this.copyLicense(copyrightFile)
await fs.chmod(copyrightFile, '0644')
})
}

/**
* Create the freedesktop.org .desktop file for the package.
*
* See: http://standards.freedesktop.org/desktop-entry-spec/latest/
*/
createDesktopFile () {
async createDesktopFile () {
const templatePath = this.options.desktopTemplate || this.defaultDesktopTemplatePath
const baseDir = path.join(this.stagingDir, this.baseAppDir, 'share', 'applications')
return desktop.createDesktopFile(templatePath, baseDir, this.appIdentifier, this.options)
Expand All @@ -188,17 +187,17 @@ class ElectronInstaller {
/**
* Create temporary directory where the contents of the package will live.
*/
createStagingDir () {
async createStagingDir () {
debug('Creating staging directory')

return tmp.dir({ prefix: 'electron-', unsafeCleanup: true })
.then(dir => {
this.stagingDir = path.join(dir.path, `${this.appIdentifier}_${this.options.version}_${this.options.arch}`)
return fs.ensureDir(this.stagingDir, '0755')
}).catch(error.wrapError('creating staging directory'))
return error.wrapError('creating staging directory', async () => {
const dir = await tmp.dir({ prefix: 'electron-installer-', unsafeCleanup: true })
this.stagingDir = path.join(dir.path, `${this.appIdentifier}_${this.options.version}_${this.options.arch}`)
return fs.ensureDir(this.stagingDir, '0755')
})
}

createTemplatedFile (templatePath, dest, filePermissions) {
async createTemplatedFile (templatePath, dest, filePermissions) {
return template.createTemplatedFile(templatePath, dest, this.options, filePermissions)
}

Expand All @@ -212,23 +211,25 @@ class ElectronInstaller {
/**
* Move the package to the specified destination.
*/
movePackage () {
async movePackage () {
debug('Moving package to destination')

return glob(this.packagePattern)
.then(files => Promise.all(files.map(file => {
return error.wrapError('moving package files', async () => {
const files = await glob(this.packagePattern)
return Promise.all(files.map(async file => {
const renameTemplate = this.options.rename(this.options.dest, path.basename(file))
const dest = _.template(renameTemplate)(this.options)
debug(`Moving file ${file} to ${dest}`)
return fs.move(file, dest, { clobber: true })
}))).catch(error.wrapError('moving package files'))
}))
})
}

/**
* For Electron versions that support the setuid sandbox on Linux, changes the permissions of
* the `chrome-sandbox` executable as appropriate.
*/
updateSandboxHelperPermissions () {
async updateSandboxHelperPermissions () {
return updateSandboxHelperPermissions(this.stagingAppDir)
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/readelectronversion.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const path = require('path')
* The content of the version file post-4.0 is just the version.
* Both of these are acceptable to the `semver` module.
*/
module.exports = function readElectronVersion (appDir) {
return fs.readFile(path.resolve(appDir, 'version'))
.then(tag => tag.toString().trim())
module.exports = async function readElectronVersion (appDir) {
const tag = await fs.readFile(path.resolve(appDir, 'version'))
return tag.toString().trim()
}
21 changes: 10 additions & 11 deletions src/readmetadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const fs = require('fs-extra')
const path = require('path')
const { wrapError } = require('./error')

function readPackageJSONFromUnpackedApp (options) {
async function readPackageJSONFromUnpackedApp (options) {
const appPackageJSONPath = path.join(options.src, 'resources', 'app', 'package.json')
options.logger(`Reading package metadata from ${appPackageJSONPath}`)

Expand All @@ -25,16 +25,15 @@ function readPackageJSONFromUnpackedApp (options) {
* * `logger`: function that handles debug messages, e.g.,
* `debug('electron-installer-something:some-module')`
*/
module.exports = function readMetadata (options) {
module.exports = async function readMetadata (options) {
const appAsarPath = path.join(options.src, 'resources/app.asar')

return fs.pathExists(appAsarPath)
.then(asarExists => {
if (asarExists) {
options.logger(`Reading package metadata from ${appAsarPath}`)
return JSON.parse(asar.extractFile(appAsarPath, 'package.json'))
} else {
return readPackageJSONFromUnpackedApp(options)
}
}).catch(wrapError('reading package metadata'))
return wrapError('reading package metadata', async () => {
if (await fs.pathExists(appAsarPath)) {
options.logger(`Reading package metadata from ${appAsarPath}`)
return JSON.parse(asar.extractFile(appAsarPath, 'package.json'))
} else {
return readPackageJSONFromUnpackedApp(options)
}
})
}
Loading

0 comments on commit dfcab00

Please sign in to comment.