create-desktop-shortcuts
Version:
Easy API to create desktop shortcuts with Node
318 lines (284 loc) • 9.09 kB
JavaScript
;
/**
* @file The core functionality of the library.
* @author TheJaredWilcurt
*/
const fs = require('fs');
const path = require('path');
const exec = require('child_process').execSync;
const spawn = require('child_process').spawnSync;
const helpers = require('./helpers.js');
const { OPTIONS } = require('../api-type-definitions.js');
const library = {
// LINUX
/**
* Creates the text to be stored in the shortcut file based on
* the user's options.
*
* @example
* let fileData = generateLinuxFileData(options);
*
* @param {OPTIONS} options User's options object
* @return {string} The data to be stored in the shortcut file
*/
generateLinuxFileData: function (options) {
if (!options || !options.linux || !options.linux.filePath) {
return '';
}
// Set defaults
let type = 'Type=Application';
let terminal = 'Terminal=false';
let exec = 'Exec="' + options.linux.filePath + '"';
let name = 'Name=' + path.parse(options.linux.filePath).name;
let comment = '';
let icon = '';
// Replace defaults if value passed in
if (options.linux.type) {
type = 'Type=' + options.linux.type;
}
if (options.linux.terminal) {
terminal = 'Terminal=' + options.linux.terminal;
}
if (options.linux.name) {
name = 'Name=' + options.linux.name;
}
if (options.linux.comment) {
comment = 'comment=' + options.linux.comment;
}
if (options.linux.icon) {
icon = 'Icon=' + options.linux.icon;
}
if (options.linux.arguments) {
exec = exec + ' ' + options.linux.arguments;
}
// File format details:
// https://wiki.archlinux.org/index.php/Desktop_entries
const fileContents = [
'#!/user/bin/env xdg-open',
'[Desktop Entry]',
'Version=1.0',
type,
terminal,
exec,
name,
comment,
icon
].filter(Boolean).join('\n');
return fileContents;
},
/**
* Saves a file to disk with the correct content based on
* the User's options. Optionally runs chmod to adjust
* execution permissions on the shortcut.
*
* @example
* let success = makeLinuxShortcut(options);
*
* @param {OPTIONS} options User's options object
* @return {boolean} true = successfully made Linux shortcut
*/
makeLinuxShortcut: function (options) {
const fileContents = this.generateLinuxFileData(options);
let success = true;
try {
fs.writeFileSync(options.linux.outputPath, fileContents);
} catch (error) {
success = false;
helpers.throwError(
options,
'ERROR: Could not create LINUX shortcut.\n' +
'PATH: ' + options.linux.outputPath + '\n' +
'DATA:\n' + fileContents,
error
);
}
if (success && options.linux.chmod) {
try {
fs.chmodSync(options.linux.outputPath, '755');
} catch (error) {
success = false;
helpers.throwError(options, 'ERROR attempting to change permisions of ' + options.linux.outputPath, error);
}
}
return success;
},
// WINDOWS
/**
* Returns the path to the windows.vbs file. Usefull for unit tests.
*
* @example
* let vbsPath = produceWindowsVBSPath();
*
* @return {string} The file path to the windows.vbs file
*/
produceWindowsVBSPath: function () {
return path.join(__dirname, 'windows.vbs');
},
/**
* Creates the data to be passed in to the VBScript based on user options.
* Spawns a wscript child process to create the shortcut and log if error.
*
* @example
* let success = makeWindowsShortcut(options);
*
* @param {OPTIONS} options User's options object
* @return {boolean} true = successfully made Windows shortcut
*/
makeWindowsShortcut: function (options) {
let success = true;
const vbsScript = options.windows.VBScriptPath || this.produceWindowsVBSPath();
if (!fs.existsSync(vbsScript)) {
helpers.throwError(options, 'Could not locate required "windows.vbs" file.');
success = false;
return success;
}
const windowModes = {
normal: 1,
maximized: 3,
minimized: 7
};
let outputPath = options.windows.outputPath;
let filePath = options.windows.filePath;
let args = options.windows.arguments || '';
let comment = options.windows.comment || '';
let cwd = options.windows.workingDirectory || '';
let icon = options.windows.icon;
let windowMode = windowModes[options.windows.windowMode] || windowModes.normal;
let hotkey = options.windows.hotkey || '';
// Double quotes (") are used as an escape character in VBScript, and are stripped out of any arguments passed in
function replaceDoubleQuotes (str) {
return str.split('"').join('__DOUBLEQUOTE__');
}
args = replaceDoubleQuotes(args);
comment = replaceDoubleQuotes(comment);
hotkey = replaceDoubleQuotes(hotkey);
if (!icon) {
if (
filePath.endsWith('.dll') ||
filePath.endsWith('.exe')
) {
icon = options.windows.filePath + ',0';
} else {
icon = options.windows.filePath;
}
}
let wscriptArguments = [
vbsScript,
// '/NoLogo', // Apparently this stops it from displaying a logo in the console, even though I haven't actually ever seen one
// '/B', // silent mode, but doesn't actually stop dialog alert windows from popping up on errors
outputPath,
filePath,
args,
comment,
cwd,
icon,
windowMode,
hotkey
];
try {
spawn('wscript', wscriptArguments);
} catch (error) {
success = false;
helpers.throwError(
options,
'ERROR: Could not create WINDOWS shortcut.\n' +
'TARGET: ' + options.windows.filePath + '\n' +
'PATH: ' + options.windows.outputPath + '\n',
error
);
}
return success;
},
// OSX
/**
* Creates the CLI arguments based on user options. Executes command.
* Handles errors.
*
* @example
* let success = makeOSXShortcut(options);
*
* @param {OPTIONS} options User's options object
* @return {boolean} true = successfully made OSX shortcut
*/
makeOSXShortcut: function (options) {
let success = true;
// Global OSX command to create symbolic links
const link = 'ln';
const symbolic = '-s';
let overwrite = '';
if (options.osx.overwrite) {
overwrite = '-f';
}
if (overwrite || (!overwrite && !fs.existsSync(options.osx.outputPath))) {
// https://ss64.com/osx/ln.html
let command = [
link,
overwrite,
symbolic,
'"' + options.osx.filePath + '"',
'"' + options.osx.outputPath + '"'
].filter(Boolean).join(' ');
try {
// ln -s "/Applications/Sublime Text.app" "/Users/owner/Desktop/Sublime Text"
exec(command);
} catch (error) {
success = false;
helpers.throwError(
options,
'ERROR: Could not create OSX shortcut.\n' +
'TARGET: ' + options.osx.filePath + '\n' +
'PATH: ' + options.osx.outputPath + '\n',
error
);
}
} else {
helpers.throwError(options, 'Could not create OSX shortcut because matching outputPath already exists and overwrite is false.');
}
return success;
},
// RUN
/**
* Checks the user's options and runs all desired OS scripts to create
* shortcuts. Returns true if all shortcuts were created successfully
* or false if any failed.
*
* @example
* let success = runCorrectOSs(options);
*
* @param {OPTIONS} options User's options object
* @return {boolean} true = all shortcuts created successfully
*/
runCorrectOSs: function (options) {
if (!options.windows && !options.linux && !options.osx) {
helpers.throwError(options, 'No shortcuts were created due to lack of accurate details passed in to options object', options);
return false;
}
if (options.onlyCurrentOS) {
if (process.platform === 'win32' && options.windows) {
return this.makeWindowsShortcut(options);
}
if (process.platform === 'linux' && options.linux) {
return this.makeLinuxShortcut(options);
}
if (process.platform === 'darwin' && options.osx) {
return this.makeOSXShortcut(options);
}
helpers.throwError(options, 'Unsupported platform. This library only supports process.platform of "win32", "linux" and "darwin".', options);
return false;
}
let windowsSuccess = true;
let linuxSuccess = true;
let osxSuccess = true;
if (options.windows) {
windowsSuccess = this.makeWindowsShortcut(options);
}
if (options.linux) {
linuxSuccess = this.makeLinuxShortcut(options);
}
if (options.osx) {
osxSuccess = this.makeOSXShortcut(options);
}
return windowsSuccess && linuxSuccess && osxSuccess;
}
};
module.exports = library;