Skip to content

Commit 8c081b0

Browse files
Add jsdoc (#31)
* add jsdoc config * document emojme-add * add docs dir * add emojme-sync docs * add emojme-upload docs * restructure, add emojme-download docs * add user-stats documentation * fixup linting * add docs/current dir to for ez publishing * refactor to make /docs the thing * add link in readme
1 parent 9f23aa9 commit 8c081b0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+23520
-13
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
docs/

.eslintrc.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@
99
"guard-for-in": "off",
1010
"no-restricted-syntax": "off",
1111
"no-cond-assign": "off",
12-
"no-multi-assign": "off"
12+
"no-multi-assign": "off",
13+
"max-len": [
14+
"error", 100, 2, {
15+
"ignoreUrls": true,
16+
"ignoreComments": true,
17+
"ignoreRegExpLiterals": true,
18+
"ignoreStrings": true,
19+
"ignoreTemplateLiterals": true
20+
}
21+
]
1322
},
1423
"env": {
1524
"commonjs": true,

.jsdoc.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"tags": {
3+
"allowUnknownTags": true,
4+
"dictionaries": ["jsdoc"]
5+
},
6+
"source": {
7+
"include": [".", "lib", "package.json", "README.md"],
8+
"includePattern": ".js$",
9+
"excludePattern": "(node_modules/|docs|spec)"
10+
},
11+
"plugins": [
12+
"plugins/markdown"
13+
],
14+
"templates": {
15+
"referenceTitle": "emojme",
16+
"disableSort": false,
17+
"collapse": true
18+
},
19+
"opts": {
20+
"destination": "./docs/",
21+
"encoding": "utf8",
22+
"private": true,
23+
"recurse": true,
24+
"template": "./node_modules/jsdoc-template"
25+
}
26+
}

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
A set of tools to manage your Slack emoji, either directly from the command line or in your own project. Upload em, download em, download em from one and upload em to another. Get yourself some emoji related statistics. It's all here.
44

5+
jsdocs are available at [https://jackellenberger.github.io/emojme/](https://jackellenberger.github.io/emojme/). Read em.
6+
57
## Requirements
68

79
To use emojme you don't need a bot or a workspace admin account. In fact, only regular [**user tokens**](https://api.slack.com/docs/token-types#user) work, and getting one isn't _quite_ as easy as getting other types of tokens. Limitations are:
@@ -139,7 +141,7 @@ Commands: (pick 1)
139141
var subdomains = ['mySubdomain1', 'mySubdomain2'] // can add one or multiple
140142
var tokens = ['myToken1', 'myToken2'] // can add one or multiple
141143
var addResults = await emojme.add(subdomains, tokens, addOptions);
142-
console.log(userStatsResults);
144+
console.log(addResults);
143145
/*
144146
{
145147
mySubomain1: {
@@ -166,7 +168,7 @@ Commands: (pick 1)
166168
output: true // download the adminList to ./build
167169
};
168170
var downloadResults = await emojme.download('mySubdomain', 'myToken', downloadOptions);
169-
console.log(userStatsResults);
171+
console.log(downloadResults);
170172
/*
171173
{
172174
mySubdomain: {
@@ -195,7 +197,7 @@ Commands: (pick 1)
195197
bustCache: true // get fresh lists to make sure we're not doing more lifting than we have to
196198
};
197199
var syncResults = await emojme.sync(null, null, syncOptions);
198-
console.log(userStatsResults);
200+
console.log(syncResults);
199201
/*
200202
{
201203
dstSubdomain1: {
@@ -220,7 +222,7 @@ Commands: (pick 1)
220222
prefix: 'new-' // prepend every emoji in src with "new-", e.g. "emoji" becomes "new-emoji"
221223
};
222224
var uploadResults = await emojme.upload('mySubdomain', 'myToken', uploadOptions);
223-
console.log(userStatsResults);
225+
console.log(uploadResults);
224226
/*
225227
{
226228
mySubdomain: {

docs/emojme-add.js.html

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
2+
3+
<!DOCTYPE html>
4+
<html lang="en">
5+
<head>
6+
7+
<meta charset="utf-8">
8+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
9+
10+
<title>
11+
emojme-add.js - Documentation
12+
</title>
13+
14+
<link href="https://www.braintreepayments.com/images/favicon-ccda0b14.png" rel="icon" type="image/png">
15+
16+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/highlight.min.js"></script>
17+
<script>hljs.initHighlightingOnLoad();</script>
18+
19+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
20+
21+
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
22+
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
23+
24+
25+
26+
<!-- start Mixpanel -->
27+
<script type="text/javascript">(function(e,a){if(!a.__SV){var b=window;try{var c,l,i,j=b.location,g=j.hash;c=function(a,b){return(l=a.match(RegExp(b+"=([^&]*)")))?l[1]:null};g&&c(g,"state")&&(i=JSON.parse(decodeURIComponent(c(g,"state"))),"mpeditor"===i.action&&(b.sessionStorage.setItem("_mpcehash",g),history.replaceState(i.desiredHash||"",e.title,j.pathname+j.search)))}catch(m){}var k,h;window.mixpanel=a;a._i=[];a.init=function(b,c,f){function e(b,a){var c=a.split(".");2==c.length&&(b=b[c[0]],a=c[1]);b[a]=function(){b.push([a].concat(Array.prototype.slice.call(arguments,
28+
0)))}}var d=a;"undefined"!==typeof f?d=a[f]=[]:f="mixpanel";d.people=d.people||[];d.toString=function(b){var a="mixpanel";"mixpanel"!==f&&(a+="."+f);b||(a+=" (stub)");return a};d.people.toString=function(){return d.toString(1)+".people (stub)"};k="disable time_event track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config reset people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
29+
for(h=0;h<k.length;h++)e(d,k[h]);a._i.push([b,c,f])};a.__SV=1.2;b=e.createElement("script");b.type="text/javascript";b.async=!0;b.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";c=e.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c)}})(document,window.mixpanel||[]);
30+
mixpanel.init("1919205b2da72e4da3b9b6639b444d59");</script>
31+
<!-- end Mixpanel -->
32+
</head>
33+
34+
<body>
35+
<svg style="display: none;">
36+
<defs>
37+
<symbol id="linkIcon" fill="#706d77" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
38+
<path d="M0 0h24v24H0z" fill="none"/>
39+
<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>
40+
</symbol>
41+
</defs>
42+
</svg>
43+
44+
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
45+
<label for="nav-trigger" class="navicon-button x">
46+
<div class="navicon"></div>
47+
</label>
48+
49+
<label for="nav-trigger" class="overlay"></label>
50+
51+
<div class="top-nav-wrapper">
52+
<ul>
53+
<li >
54+
<a href="index.html">
55+
56+
<svg fill="#6D6D6D" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
57+
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
58+
<path d="M0 0h24v24H0z" fill="none"/>
59+
</svg>
60+
61+
62+
</a>
63+
</li>
64+
65+
66+
67+
</ul>
68+
</div>
69+
70+
<nav>
71+
<h3 class="reference-title">
72+
emojme
73+
</h3>
74+
75+
<h3>Modules</h3><ul><li id="add-nav"><a href="module-add.html">add</a><ul class='methods'><li data-type="method" id="add-add-nav"><a href="module-add.html#~add">add</a></li></ul></li><li id="download-nav"><a href="module-download.html">download</a><ul class='methods'><li data-type="method" id="download-download-nav"><a href="module-download.html#~download">download</a></li></ul></li><li id="sync-nav"><a href="module-sync.html">sync</a><ul class='methods'><li data-type="method" id="sync-sync-nav"><a href="module-sync.html#~sync">sync</a></li></ul></li><li id="upload-nav"><a href="module-upload.html">upload</a><ul class='methods'><li data-type="method" id="upload-upload-nav"><a href="module-upload.html#~upload">upload</a></li></ul></li><li id="userStats-nav"><a href="module-userStats.html">userStats</a><ul class='methods'><li data-type="method" id="userStats-userStats-nav"><a href="module-userStats.html#~userStats">userStats</a></li></ul></li></ul>
76+
</nav>
77+
78+
<div id="main">
79+
80+
<h1 class="page-title">
81+
emojme-add.js
82+
</h1>
83+
84+
85+
86+
87+
88+
<section>
89+
<article>
90+
<pre class="prettyprint source linenums"><code>const _ = require('lodash');
91+
const commander = require('commander');
92+
93+
const EmojiAdminList = require('./lib/emoji-admin-list');
94+
const EmojiAdd = require('./lib/emoji-add');
95+
96+
const FileUtils = require('./lib/util/file-utils');
97+
const Helpers = require('./lib/util/helpers');
98+
const Cli = require('./lib/util/cli');
99+
/** @module add */
100+
101+
/**
102+
* The add response object, like other response objects, is organized by input subdomain.
103+
* @typedef {object} addResponseObject
104+
* @property {object} subdomain each subdomain passed in to add will appear as a key in the response
105+
* @property {emojiList[]} subdomain.emojiList the list of emoji added to `subdomain`, with each element reflecting the parameters passed in to `add`
106+
* @property {emojiList[]} subdomain.collisions if `options.avoidCollisions` is `false`, emoji that cannot be uploaded due to existing conflicting emoji names will exist here
107+
*/
108+
109+
/**
110+
* Add emoji described by parameters within options to the specified subdomain(s).
111+
*
112+
* Note that options can accept both aliases and original emoji at the same time, but ordering can get complicated and honestly I'd skip it if I were you. For each emoji, make sure that every descriptor (src, name, aliasFor) has a value, using `null`s for fields that are not relevant to the current emoji.
113+
*
114+
* @async
115+
* @param {string|string[]} subdomains a single or list of subdomains to add emoji to. Must match respectively to `tokens`
116+
* @param {string|string[]} tokens a single or list of tokens to add emoji to. Must match respectively to `subdomains`
117+
* @param {object} options contains singleton or arrays of emoji descriptors.
118+
* @param {string|string[]} [options.src] source image files for the emoji to be added. If no corresponding `options.name` is given, the filename will be used
119+
* @param {string|string[]} [options.name] names of the emoji to be added, overriding filenames if given, and becoming the alias name if an `options.aliasFor` is given
120+
* @param {string|string[]} [options.aliasFor] names of emoji to be aliased to `options.name`
121+
* @param {boolean} [options.avoidCollisions] if `true`, emoji being added will be renamed to not collide with existing emoji. See {@link lib/util/helpers.avoidCollisions} for logic and details // TODO: fix this link, maybe link to tests which has better examples
122+
* @param {string} [options.prefix] string to prefix all emoji being uploaded
123+
* @param {boolean} [options.bustCache] if `true`, ignore any adminList younger than 24 hours and repull slack instance's existing emoji. Can be useful for making `options.avoidCollisions` more accurate
124+
* @param {boolean} [options.output] if `false`, no files will be written during execution. Prevents saving of adminList for future use
125+
*
126+
* @returns {Promise&lt;addResponseObject>} addResponseObject result object
127+
*
128+
* @example
129+
var addOptions = {
130+
src: ['./emoji1.jpg', 'http://example.com/emoji2.png'], // upload these two images
131+
name: ['myLocalEmoji', 'myOnlineEmoji'], // call them these two names
132+
bustCache: false, // don't bother redownloading existing emoji
133+
avoidCollisions: true, // if there are similarly named emoji, change my new emoji names
134+
output: false // don't write any files
135+
};
136+
var subdomains = ['mySubdomain1', 'mySubdomain2'] // can add one or multiple
137+
var tokens = ['myToken1', 'myToken2'] // can add one or multiple
138+
var addResults = await emojme.add(subdomains, tokens, addOptions);
139+
console.log(userStatsResults);
140+
// {
141+
// mySubomain1: {
142+
// collisions: [], // only defined if avoidCollisons = false
143+
// emojiList: [
144+
// { name: 'myLocalEmoji', ... },
145+
// { name: 'myOnlineEmoji', ... },
146+
// ]
147+
// },
148+
// mySubomain2: {
149+
// collisions: [], // only defined if avoidCollisons = false
150+
// emojiList: [
151+
// { name: 'myLocalEmoji', ... },
152+
// { name: 'myOnlineEmoji', ... },
153+
// ]
154+
// }
155+
// }
156+
*/
157+
async function add(subdomains, tokens, options) {
158+
subdomains = Helpers.arrayify(subdomains);
159+
tokens = Helpers.arrayify(tokens);
160+
options = options || {};
161+
const aliases = Helpers.arrayify(options.aliasFor);
162+
const names = Helpers.arrayify(options.name);
163+
const sources = Helpers.arrayify(options.src);
164+
let inputEmoji = []; let name; let alias; let
165+
source;
166+
167+
while (aliases.length || sources.length) {
168+
name = names.shift();
169+
if (source = sources.shift()) {
170+
inputEmoji.push({
171+
is_alias: 0,
172+
url: source,
173+
name: name || source.match(/(?:.*\/)?(.*).(jpg|jpeg|png|gif)/)[1],
174+
});
175+
} else {
176+
alias = aliases.shift();
177+
inputEmoji.push({
178+
is_alias: 1,
179+
alias_for: alias,
180+
name,
181+
});
182+
}
183+
}
184+
185+
if (names.length || _.find(inputEmoji, ['name', undefined])) {
186+
return Promise.reject(new Error('Invalid input. Either not all inputs have been consumed, or not all emoji are well formed. Consider simplifying input, or padding input with `null` values.'));
187+
}
188+
189+
const [authPairs] = Helpers.zipAuthPairs(subdomains, tokens);
190+
191+
const addPromises = authPairs.map(async (authPair) => {
192+
const emojiAdd = new EmojiAdd(...authPair);
193+
const existingEmojiList = await new EmojiAdminList(...authPair, options.output)
194+
.get(options.bustCache);
195+
const existingNameList = existingEmojiList.map(e => e.name);
196+
197+
if (options.prefix) {
198+
inputEmoji = Helpers.applyPrefix(inputEmoji, options.prefix);
199+
}
200+
201+
if (options.avoidCollisions) {
202+
inputEmoji = Helpers.avoidCollisions(inputEmoji, existingEmojiList);
203+
}
204+
205+
const [collisions, emojiToUpload] = _.partition(inputEmoji,
206+
emoji => existingNameList.includes(emoji.name));
207+
208+
return emojiAdd.upload(emojiToUpload).then((uploadResult) => {
209+
if (uploadResult.errorList &amp;&amp; uploadResult.errorList.length > 1 &amp;&amp; options.output) {
210+
FileUtils.writeJson(`./build/${this.subdomain}.emojiUploadErrors.json`, uploadResult.errorList);
211+
}
212+
return Object.assign({}, uploadResult, { collisions });
213+
});
214+
});
215+
216+
return Helpers.formatResultsHash(await Promise.all(addPromises));
217+
}
218+
219+
function addCli() {
220+
const program = new commander.Command();
221+
222+
Cli.requireAuth(program);
223+
Cli.allowIoControl(program);
224+
Cli.allowEmojiAlterations(program)
225+
.option('--src &lt;value>', 'source image/gif/#content for emoji you\'d like to upload', Cli.list, null)
226+
.option('--name &lt;value>', 'name of the emoji from --src that you\'d like to upload', Cli.list, null)
227+
.option('--alias-for &lt;value>', 'name of the emoji you\'d like --name to be an alias of. Specifying this will negate --src', Cli.list, null)
228+
.parse(process.argv);
229+
230+
return add(program.subdomain, program.token, {
231+
src: program.src,
232+
name: program.name,
233+
aliasFor: program.aliasFor,
234+
bustCache: program.bustCache,
235+
avoidCollisions: program.avoidCollisions,
236+
prefix: program.prefix,
237+
output: program.output,
238+
});
239+
}
240+
241+
242+
if (require.main === module) {
243+
addCli();
244+
}
245+
246+
module.exports = {
247+
add,
248+
addCli,
249+
};
250+
</code></pre>
251+
</article>
252+
</section>
253+
254+
255+
256+
257+
</div>
258+
259+
<br class="clear">
260+
261+
<footer>
262+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a>
263+
</footer>
264+
265+
<script src="scripts/linenumber.js"></script>
266+
<script src="scripts/pagelocation.js"></script>
267+
268+
269+
270+
</body>
271+
</html>

0 commit comments

Comments
 (0)