├── .gitignore ├── README.md ├── api.js ├── fileIo.js ├── modules ├── commands.js ├── error.js ├── execute.js ├── group.js ├── help.js ├── list.js └── print.js ├── package.json ├── scripts └── preuninstall.js └── web /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | store.json 3 | local.txt 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | npm version 2 | 3 | # Web Launcher 4 | 5 | A CLI for quickly launching the websites you use. 6 | 7 |
8 | 9 | 10 | 11 |
12 | 13 | **Requires Node version 6 or higher. Tested on Mac.** 14 | 15 |
16 | 17 | Open and manage your web environments from the command line. Like using a bunch of aliases/Bash scripts or installing a chrome extension that supports link sets, but in a command line interface. 18 | 19 | ## Installation 20 | 21 | ```shell 22 | 23 | npm install -g web-launcher 24 | 25 | ``` 26 | 27 | ## Usage 28 | 29 | Run the help command at any level to get instructions. 30 | 31 | ```shell 32 | 33 | web help 34 | 35 | ``` 36 | 37 | would output 38 | 39 | ``` 40 | list [ls] List all groups. 41 | 42 | 43 | groups Add or remove a group. 44 | 45 | 46 | run [open] {groupName} Open group in default browser. 47 | 48 | 49 | help Print help menu. 50 | 51 | 52 | {groupName} Run, add or remove sites from specified group. 53 | 54 | Key: [alias] {arg} ?{optionalArg} 55 | ``` 56 | -------------------------------------------------------------------------------- /api.js: -------------------------------------------------------------------------------- 1 | const fileIo = require('./fileIo') 2 | const error = require('./modules/error') 3 | const chalk = require('chalk') 4 | 5 | class api { 6 | constructor () { 7 | this.state = fileIo.readInitialState() 8 | } 9 | 10 | save () { 11 | return fileIo.writeState(this.state) 12 | } 13 | 14 | // lists 15 | 16 | getList (name) { 17 | const results = this.state.filter( (list) => { 18 | return list.name === name 19 | }) 20 | 21 | return results.length ? results[0] : null 22 | } 23 | 24 | listExists (name) { 25 | return !!this.getList(name) 26 | } 27 | 28 | addList (name) { 29 | const list = this.getList(name) 30 | 31 | if (!list) { 32 | this.state.push({ name, sites: [] }) 33 | } else { 34 | throw 'List already exists.' 35 | } 36 | } 37 | 38 | removeList (name) { 39 | const listExists = this.listExists(name) 40 | if (listExists) { 41 | this.state = this.state.filter( list => (list.name !== name)) 42 | } else { 43 | throw 'List does not exist.' 44 | } 45 | } 46 | 47 | // list items 48 | 49 | get (name) { 50 | const list = this.getList(name) 51 | 52 | if (list) { 53 | return list 54 | } else { 55 | return this.state 56 | } 57 | } 58 | 59 | addItems (name, items) { 60 | const list = this.getList(name) 61 | 62 | if (list) { 63 | list.sites = list.sites.concat(items.map((site) => { 64 | // replace https:// with http:// 65 | if (site.indexOf('https://') === 0) { 66 | return `http://${site.slice('https://'.length)}` 67 | } else if (site.indexOf('http://') === 0){ 68 | return site 69 | } 70 | return `http://${site}` 71 | })); 72 | } else { 73 | throw 'List not found.' 74 | } 75 | } 76 | 77 | removeItems (name, indexes) { 78 | const list = this.getList(name) 79 | 80 | if (list) { 81 | if (indexes) { 82 | list.sites = list.sites.filter( (item, index) => { 83 | return !indexes.includes(index) 84 | }) 85 | } else { 86 | list.sites = []; 87 | } 88 | } else { 89 | throw 'List not found.' 90 | } 91 | } 92 | } 93 | 94 | module.exports = new api(); 95 | -------------------------------------------------------------------------------- /fileIo.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | function getUserHome() { 4 | return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME']; 5 | } 6 | 7 | function readInitialState() { 8 | try { 9 | return JSON.parse(fs.readFileSync(`${getUserHome()}/.web-launcher/store.json`, 'utf8')) 10 | } catch (err) { 11 | switch (err.code) { 12 | case 'ENOENT': 13 | return [] 14 | case 'EACCES': 15 | throw 'Error: Cannot read store file.' 16 | default: 17 | throw `Error: ${err}` 18 | } 19 | } 20 | } 21 | 22 | function writeState (state) { 23 | 24 | if (!fs.existsSync(`${getUserHome()}/.web-launcher`)){ 25 | fs.mkdirSync(`${getUserHome()}/.web-launcher`); 26 | } 27 | 28 | return new Promise( (resolve, reject) => { 29 | fs.writeFile(`${getUserHome()}/.web-launcher/store.json`, JSON.stringify(state), (err) => { 30 | if (!err) { 31 | resolve(true) 32 | } else { 33 | reject(err) 34 | } 35 | }) 36 | }) 37 | } 38 | 39 | function deleteState () { 40 | if (fs.existsSync(`${getUserHome()}/.web-launcher`)) { 41 | return new Promise( (resolve, reject) => { 42 | fs.unlink(`${getUserHome()}/.web-launcher/store.json`, (err) => { 43 | if (!err) { 44 | fs.rmdir(`${getUserHome()}/.web-launcher`, (err) => { 45 | if (!err) { 46 | resolve(true) 47 | } else { 48 | reject(err) 49 | } 50 | }) 51 | } else { 52 | reject(err) 53 | } 54 | }) 55 | }) 56 | } 57 | return Promise.resolve(true) 58 | } 59 | 60 | module.exports = { readInitialState, writeState, deleteState }; 61 | -------------------------------------------------------------------------------- /modules/commands.js: -------------------------------------------------------------------------------- 1 | module.exports = () => 2 2 | -------------------------------------------------------------------------------- /modules/error.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk') 2 | 3 | module.exports = (message) => { 4 | console.log(chalk.red('///////////')) 5 | console.log(chalk.red(message)) 6 | console.log(chalk.red('///////////')) 7 | } 8 | -------------------------------------------------------------------------------- /modules/execute.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').exec 2 | const chalk = require('chalk') 3 | const error = require('./error') 4 | 5 | module.exports = (api, list) => { 6 | 7 | try { 8 | if (list) { 9 | executeList(api, api.get(list)) 10 | } else { 11 | api.get().forEach(executeList.bind(api)) 12 | } 13 | } catch (e) { 14 | error(e) 15 | } 16 | 17 | } 18 | 19 | function executeList (api, list) { 20 | const openCommands = { 21 | darwin: 'open', 22 | linux: 'xdg-open' 23 | } 24 | 25 | function getCommandString(platform, sites) { 26 | // run commands separated by & for windows 27 | if (platform === 'win32') return sites.map(site => `start ${site}`).join(' & ') 28 | return `${openCommands[process.platform]} ${sites.join(' ')}` 29 | } 30 | 31 | if (list.sites.length) { 32 | console.log(chalk.yellow('\launching...')) 33 | exec(getCommandString(process.platform, list.sites)) 34 | console.log(chalk.green('Done.')) 35 | } else { 36 | console.log(chalk.green(`No sites to run in `) + chalk.cyan(list.name)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /modules/group.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk') 2 | const print = require('./print') 3 | const error = require('./error') 4 | const execute = require('./execute') 5 | const help = require('./help') 6 | 7 | module.exports = (api, args, collection) => { 8 | 9 | const command = args.splice(0, 1)[0] 10 | 11 | switch (command) { 12 | case 'list': 13 | case 'ls': 14 | print(api, collection) 15 | break 16 | 17 | case 'add': 18 | case '-a': 19 | addSites(api, args, collection) 20 | break 21 | 22 | case 'clear': 23 | case 'remove': 24 | case 'delete': 25 | case '-d': 26 | clearSites(api, args, collection) 27 | break 28 | 29 | case 'run': 30 | case 'open': 31 | execute(api, collection) 32 | break 33 | 34 | default: 35 | error('Not sure what to do there.') 36 | help('group') 37 | } 38 | } 39 | 40 | function addSites (api, args, collection) { 41 | if (args.length) { 42 | api.addItems(collection, args) 43 | } else { 44 | error(`${chalk.underline.bgRed('add')} must be followed by URLS to be added.`) 45 | } 46 | } 47 | 48 | function clearSites (api, args, collection) { 49 | const validatedArgs = args 50 | .map( arg => parseInt(arg)) 51 | .filter( arg => !isNaN(arg)) 52 | 53 | if (args.length && validatedArgs.length === args.length) { 54 | api.removeItems(collection, validatedArgs) 55 | } else if (!args.length) { 56 | api.removeItems(collection) 57 | } else { 58 | error(`${chalk.underline.bgRed('clear')} must have 0 or more integer arguments.`) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /modules/help.js: -------------------------------------------------------------------------------- 1 | const error = require('./error') 2 | const chalk = require('chalk') 3 | 4 | module.exports = (section) => { 5 | switch (section) { 6 | case 'root': 7 | case undefined: 8 | printTopLevel() 9 | break 10 | 11 | case 'groups': 12 | printGroup() 13 | break 14 | 15 | case 'group': 16 | printGroupInstance() 17 | break 18 | } 19 | } 20 | 21 | function printTopLevel () { 22 | console.log(chalk.yellow(` 23 | 24 | list [ls] \t\t\t List all groups. 25 | \n 26 | groups \t\t\t Add or remove a group. 27 | \n 28 | run [open] {groupName} \t\t Open group in default browser. 29 | \n 30 | help \t\t\t Print help menu. 31 | \n 32 | {groupName} \t\t\t Run, add or remove sites from specified group. 33 | `)) 34 | printKey() 35 | } 36 | 37 | function printGroupInstance () { 38 | console.log(chalk.green('> web {groupName} :')) 39 | console.log(chalk.yellow(` 40 | list [ls] \t\t\t\t List all sites in the group. 41 | \n 42 | add [-a] {site1} {site2} ... \t Add sites to group. 43 | \n 44 | clear [delete] [-d] ?{siteIndex} \t Remove sites. If no args, removes all. 45 | \n 46 | run [open] \t\t\t\t Open group in default browser. 47 | \n 48 | help \t\t\t\t Print this help menu. 49 | `)) 50 | printKey() 51 | } 52 | 53 | function printGroup () { 54 | console.log(chalk.green('> web groups :')) 55 | console.log(chalk.yellow(` 56 | list [ls] \t\t\t\t List all sites in the group. 57 | \n 58 | add [-a] {groupName} \t\t\t Add group with specified name. 59 | \n 60 | clear [delete] [-d] {groupName}\t\t Remove group with specified name. 61 | \n 62 | help \t\t\t\t Print this help menu. 63 | `)) 64 | printKey() 65 | } 66 | 67 | function printKey () { 68 | console.log(chalk.cyan('Key: [alias] {arg} ?{optionalArg}\n')) 69 | } 70 | -------------------------------------------------------------------------------- /modules/list.js: -------------------------------------------------------------------------------- 1 | const error = require('./error') 2 | const help = require('./help') 3 | 4 | module.exports = (api, args) => { 5 | 6 | const command = args.splice(0, 1)[0] 7 | 8 | switch (command) { 9 | case 'add': 10 | case '-a': 11 | addList(api, args) 12 | break 13 | 14 | case 'clear': 15 | case 'delete': 16 | case '-d': 17 | removeList(api, args) 18 | break 19 | 20 | case 'help': 21 | default: 22 | error('Not sure what to do there.') 23 | help('groups') 24 | } 25 | } 26 | 27 | function addList (api, args) { 28 | try { 29 | api.addList(args[0]) 30 | } catch (e) { 31 | error(e) 32 | } 33 | } 34 | 35 | function removeList (api, args) { 36 | try { 37 | api.removeList(args[0]) 38 | } catch (e) { 39 | error(e) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /modules/print.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk') 2 | const error = require('./error') 3 | 4 | module.exports = (api, list) => { 5 | 6 | try { 7 | if (list) { 8 | printCollection(api.get(list)) 9 | console.log('\n') 10 | } else { 11 | const lists = api.get() 12 | if (lists.length) { 13 | lists.forEach(printCollection) 14 | console.log('\n') 15 | } else { 16 | console.log(chalk.green('\nNo groups found.\n')) 17 | } 18 | } 19 | } catch (e) { 20 | error(e) 21 | } 22 | } 23 | 24 | function printCollection (list) { 25 | console.log(chalk.green(`\n${list.name}:\n`)) 26 | 27 | if (list.sites.length) { 28 | list.sites.forEach( (item, index) => { 29 | console.log(`${chalk.yellow(`${index}:`)}\t${chalk.cyan(item)}`) 30 | }) 31 | } else { 32 | console.log(chalk.cyan('No sites stored.')) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-launcher", 3 | "version": "0.0.19", 4 | "description": "Easily open and manage website lists from the command line", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "preuninstall" : "node scripts/preuninstall.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/briandennis/WebLauncher" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/briandennis/GoodMorning/issues" 18 | }, 19 | "homepage": "https://github.com/briandennis/GoodMorning#readme", 20 | "dependencies": { 21 | "chalk": "^1.1.3" 22 | }, 23 | "bin": { 24 | "web": "web" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scripts/preuninstall.js: -------------------------------------------------------------------------------- 1 | const { deleteState } = require('../fileIo'); 2 | const chalk = require('chalk') 3 | 4 | deleteState().then(() => { 5 | console.log(chalk.yellow('Removing WebLauncher files.')); 6 | console.log(chalk.green('Beginning uninstall.')); 7 | }).catch((err) => { 8 | console.log(`${chalk.red("Failure")} uninstalling WebLauncher with error: ${err}. Exiting.`) 9 | }); 10 | -------------------------------------------------------------------------------- /web: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const chalk = require('chalk') 4 | const api = require('./api') 5 | 6 | const group = require('./modules/group') 7 | const commands = require('./modules/commands') 8 | const execute = require('./modules/execute') 9 | const help = require('./modules/help') 10 | const print = require('./modules/print') 11 | const list = require('./modules/list') 12 | const error = require('./modules/error') 13 | 14 | const command = process.argv[2] 15 | const args = process.argv.slice(3) 16 | 17 | switch (command) { 18 | 19 | case 'ls': 20 | case 'list': 21 | if(args.length !== 0) { 22 | error('Nope, not using that right!') 23 | help() 24 | break 25 | } 26 | print(api) 27 | break 28 | 29 | case 'groups': 30 | list(api, args) 31 | break 32 | 33 | case 'open': 34 | case 'run': 35 | if (args.length === 1) { 36 | if (api.listExists(args[0])) { 37 | execute(api, args[0]) 38 | break 39 | } else { 40 | error('Group not found.') 41 | break 42 | } 43 | } else { 44 | error('Nope, not using that right!') 45 | help() 46 | break 47 | } 48 | 49 | case 'help': 50 | case undefined: 51 | help() 52 | break 53 | 54 | default: 55 | if (api.listExists(command)) { 56 | group(api, args, command) 57 | } else { 58 | error('Group not found. Did you mean to do something else?') 59 | help('root') 60 | } 61 | } 62 | 63 | api.save() 64 | --------------------------------------------------------------------------------