diff --git a/.gitattributes b/.gitattributes index 391f0a4..6313b56 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ -* text=auto -*.js text eol=lf +* text=auto eol=lf diff --git a/.travis.yml b/.travis.yml index 225084f..6d9d707 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ os: osx language: node_js node_js: + - '12' - '10' - '8' diff --git a/cli.js b/cli.js index f44ab58..fe9c616 100755 --- a/cli.js +++ b/cli.js @@ -117,7 +117,7 @@ async function init() { ee.on('finish', async () => { try { ora.text = 'Replacing DMG icon'; - // Seticon is a native tool to change files icons (Source: https://github.com/sveinbjornt/osxiconutils) + // `seticon`` is a native tool to change files icons (Source: https://github.com/sveinbjornt/osxiconutils) await execa(path.join(__dirname, 'seticon'), [composedIconPath, dmgPath]); ora.text = 'Code signing DMG'; @@ -128,9 +128,9 @@ async function init() { } else if (stdout.includes('Mac Developer:')) { identity = 'Mac Developer'; } else { - const err = new Error(); - err.stderr = 'No usable identity found'; - throw err; + const error = new Error(); + error.stderr = 'No usable identity found'; + throw error; } await execa('codesign', ['--sign', identity, dmgPath]); diff --git a/compose-icon.js b/compose-icon.js index 80ca74f..d34fe1c 100644 --- a/compose-icon.js +++ b/compose-icon.js @@ -9,7 +9,8 @@ const readFile = promisify(fs.readFile); const writeFile = promisify(fs.writeFile); const filterMap = (map, filterFn) => Object.entries(map).filter(filterFn).reduce((out, [key, item]) => ({...out, [key]: item}), {}); -// Drive icon from /System/Library/Extensions/IOStorageFamily.kext/Contents/Resources/Removable.icns + +// Drive icon from `/System/Library/Extensions/IOStorageFamily.kext/Contents/Resources/Removable.icns`` const baseDiskIconPath = `${__dirname}/disk-icon.icns`; async function composeIcon(type, appIcon, mountIcon, composedIcon) { @@ -20,11 +21,13 @@ async function composeIcon(type, appIcon, mountIcon, composedIcon) { // Change the perspective of the app icon to match the mount drive icon appIcon = appIcon.out('-matte').out('-virtual-pixel', 'transparent').out('-distort', 'Perspective', `1,1 ${appIconSize.width * 0.08},1 ${appIconSize.width},1 ${appIconSize.width * 0.92},1 1,${appIconSize.height} 1,${appIconSize.height} ${appIconSize.width},${appIconSize.height} ${appIconSize.width},${appIconSize.height}`); + // Resize the app icon to fit it inside the mount icon, aspect ration should not be kept to create the perspective illution appIcon = appIcon.resize(appIconSize.width / 1.7, appIconSize.height / 1.78, '!'); const tempAppIconPath = tempy.file({extension: 'png'}); await promisify(appIcon.write.bind(appIcon))(tempAppIconPath); + // Compose the two icons const iconGravityFactor = mountIconSize.height * 0.155; mountIcon = mountIcon.composite(tempAppIconPath).gravity('Center').geometry(`+0-${iconGravityFactor}`); @@ -40,14 +43,16 @@ const hasGm = async () => { if (error.code === 'ENOENT') { return false; } + throw error; } }; -module.exports = async function (appIconPath) { +module.exports = async appIconPath => { if (!await hasGm()) { return baseDiskIconPath; } + const baseDiskIcons = filterMap(icns.parse(await readFile(baseDiskIconPath)), ([key]) => icns.isImageType(key)); const appIcon = filterMap(icns.parse(await readFile(appIconPath)), ([key]) => icns.isImageType(key)); @@ -56,9 +61,13 @@ module.exports = async function (appIconPath) { if (baseDiskIcons[type]) { return composeIcon(type, icon, baseDiskIcons[type], composedIcon); } + console.warn('There is no base image for this type', type); })); + const tempComposedIcon = tempy.file({extension: 'icns'}); + await writeFile(tempComposedIcon, icns.format(composedIcon)); + return tempComposedIcon; }; diff --git a/icon-example-app.png b/icon-example-app.png index e4d9f2d..f38163c 100644 Binary files a/icon-example-app.png and b/icon-example-app.png differ diff --git a/icon-example.png b/icon-example.png index dfae9be..af1c697 100644 Binary files a/icon-example.png and b/icon-example.png differ diff --git a/package.json b/package.json index 9b59cab..193eb19 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "files": [ "cli.js", + "compose-icon.js", "assets", "disk-icon.icns", "seticon" @@ -45,10 +46,10 @@ "meow": "^5.0.0", "ora": "^3.0.0", "plist": "^3.0.1", - "tempy": "^0.2.1" + "tempy": "^0.3.0" }, "devDependencies": { - "ava": "^0.25.0", - "xo": "^0.23.0" + "ava": "^1.4.1", + "xo": "^0.24.0" } } diff --git a/readme.md b/readme.md index 822a7f6..04f99cf 100644 --- a/readme.md +++ b/readme.md @@ -49,18 +49,20 @@ It will try to code sign the DMG, but the DMG is still created and fine even if ### DMG Icon -[GraphicsMagick](http://www.graphicsmagick.org/) is required to create the DMG icon based on the application icon and macOS mounted device icon. +[GraphicsMagick](http://www.graphicsmagick.org) is required to create the custom DMG icon that's based on the app icon and the macOS mounted device icon. -### Steps using Brew -```bash -brew install imagemagick -brew install graphicsmagick +#### Steps using Homebrew + +``` +$ brew install graphicsmagick imagemagick ``` -### Icon Example +#### Icon Example + +Original icon → DMG icon + + - - ## License