1 | #!/usr/bin/env node
|
2 |
|
3 | 'use strict';
|
4 |
|
5 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
6 |
|
7 | require('core-js/modules/es.array.iterator');
|
8 | var _reduce = _interopDefault(require('ramda/src/reduce'));
|
9 | var program = _interopDefault(require('commander'));
|
10 | require('core-js/modules/es.string.replace');
|
11 | var _map = _interopDefault(require('ramda/src/map'));
|
12 | var _prop = _interopDefault(require('ramda/src/prop'));
|
13 | var _descend = _interopDefault(require('ramda/src/descend'));
|
14 | var _sort = _interopDefault(require('ramda/src/sort'));
|
15 | var _lensProp = _interopDefault(require('ramda/src/lensProp'));
|
16 | var _set = _interopDefault(require('ramda/src/set'));
|
17 | var _compose = _interopDefault(require('ramda/src/compose'));
|
18 | var _multiply = _interopDefault(require('ramda/src/multiply'));
|
19 | var _always = _interopDefault(require('ramda/src/always'));
|
20 | var _ifElse = _interopDefault(require('ramda/src/ifElse'));
|
21 | var _divide = _interopDefault(require('ramda/src/divide'));
|
22 | var _flip = _interopDefault(require('ramda/src/flip'));
|
23 | var _any = _interopDefault(require('ramda/src/any'));
|
24 | var _filter = _interopDefault(require('ramda/src/filter'));
|
25 | var _find = _interopDefault(require('ramda/src/find'));
|
26 | var _endsWith = _interopDefault(require('ramda/src/endsWith'));
|
27 | var _startsWith = _interopDefault(require('ramda/src/startsWith'));
|
28 | var _anyPass = _interopDefault(require('ramda/src/anyPass'));
|
29 | var _replace = _interopDefault(require('ramda/src/replace'));
|
30 | var _is = _interopDefault(require('ramda/src/is'));
|
31 | var snippets = _interopDefault(require('../lib/snippets.json'));
|
32 | var _pick = _interopDefault(require('ramda/src/pick'));
|
33 | var _toPairs = _interopDefault(require('ramda/src/toPairs'));
|
34 | var _join = _interopDefault(require('ramda/src/join'));
|
35 | var Renderer = _interopDefault(require('marked-terminal'));
|
36 | var chalk = _interopDefault(require('chalk'));
|
37 | var marked = _interopDefault(require('marked'));
|
38 | var cliHighlight = require('cli-highlight');
|
39 | var clipboardy = require('clipboardy');
|
40 |
|
41 | const enforceSingleNewLine = x => _is(String, x) ? _compose(_replace(/[\r\n]+/g, '\n'), _replace(/[\r\n]*$/, ''))(x) : x;
|
42 | const randomNumberInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
|
43 | const startsOrEndsWith = (val, query) => _anyPass([_startsWith, _endsWith])(query, val);
|
44 |
|
45 | const fuzzyMatch = query => x => new RegExp(query).test(x.id);
|
46 |
|
47 | const getSnippet = (snippets, id) => _find(x => x.id === id, snippets);
|
48 | const getSnippetsByTag = (snippets, id) => _filter(x => _any(y => y === id, x.tags), snippets);
|
49 | const randomSnippet = snippets => snippets[randomNumberInRange(0, snippets.length - 1)];
|
50 |
|
51 | const computeRelevance = (id, query) => {
|
52 | const base = query.length * 10;
|
53 | const diffInChars = id.replace(query, '').length + 1;
|
54 |
|
55 | const divideByDiffInChars = _flip(_divide)(diffInChars);
|
56 |
|
57 | const multi = _ifElse(_always(startsOrEndsWith(id, query)), _multiply(0.75), _multiply(0.5));
|
58 |
|
59 | if (id === query) return base;
|
60 | return _compose(divideByDiffInChars, multi)(base);
|
61 | };
|
62 |
|
63 | const setSearchRelevance = query => x => _set(_lensProp('relevance'), computeRelevance(x.id, query), x);
|
64 |
|
65 | const searchSnippets = (snippets, query) => _compose(_sort(_descend(_prop('relevance'))), _map(setSearchRelevance(query)), _filter(fuzzyMatch(query)))(snippets);
|
66 |
|
67 | marked.setOptions({
|
68 | renderer: new Renderer()
|
69 | });
|
70 | const colorizedPrint = x => {
|
71 | const printMap = {
|
72 | code: y => cliHighlight.highlight(y, {
|
73 | language: 'javascript'
|
74 | }),
|
75 | description: y => enforceSingleNewLine(marked(y)),
|
76 | example: y => cliHighlight.highlight(y, {
|
77 | language: 'javascript'
|
78 | }),
|
79 | id: y => chalk.magenta.bold(y),
|
80 | tags: _compose(chalk.gray.italic, _join(', '))
|
81 | };
|
82 |
|
83 | const print = y => _compose(_replace(/\n$/, ''), _join('\n'), _map(([k, v]) => printMap[k](v) + (k === 'id' ? '' : '\n')), _toPairs)(y);
|
84 |
|
85 | return _compose(_join('\n\n'), _map(print))(x);
|
86 | };
|
87 | const printSnippet = ({
|
88 | cp,
|
89 | layout,
|
90 | json
|
91 | }, x) => {
|
92 | const arr = _is(Array, x) ? x : [x];
|
93 | const layoutMap = {
|
94 | c: 'code',
|
95 | d: 'description',
|
96 | e: 'example',
|
97 | i: 'id',
|
98 | t: 'tags'
|
99 | };
|
100 |
|
101 | const keysByLayout = _map(k => layoutMap[k])(Array.from(layout || 'itced'));
|
102 |
|
103 | if (x == null) return '';
|
104 |
|
105 | if (cp) {
|
106 | clipboardy.writeSync(_compose(_join('\n'), _map(_prop('code')))(arr));
|
107 | }
|
108 |
|
109 | const pickedSnippet = _map(_pick(keysByLayout))(arr);
|
110 |
|
111 | return _compose(console.log, json ? JSON.stringify : colorizedPrint)(pickedSnippet);
|
112 | };
|
113 |
|
114 | var version = "1.5.1";
|
115 |
|
116 | const isTest = process.env.NODE_ENV === 'test';
|
117 | const actions = {
|
118 | random: opts => printSnippet(opts, randomSnippet(snippets)),
|
119 | search: (opts, query) => printSnippet(opts, searchSnippets(snippets, query)),
|
120 | tag: (opts, id) => printSnippet(opts, getSnippetsByTag(snippets, id)),
|
121 | view: (opts, id) => printSnippet(opts, getSnippet(snippets, id))
|
122 | };
|
123 |
|
124 | const addCommand = settings => _reduce((acc, [key, ...args]) => acc[key](...args), program, settings);
|
125 |
|
126 | const addAction = action => ['action', (...args) => ((input, opts = {}) => {
|
127 | if (!input) return program.outputHelp();
|
128 | return isTest ?
|
129 |
|
130 |
|
131 | console.log(JSON.stringify([action, input, !!opts.cp, !!opts.json, opts.layout])) : actions[action](opts, input);
|
132 | })(...(action === 'random' ? [true, args[0]] : args))];
|
133 |
|
134 | const commonOptions = [['option', '-c, --cp', 'copy code to clipboard', false], ['option', '-j, --json', 'output in json format', false], ['option', '-l, --layout <layout>', 'print in specified layout', 'itced']];
|
135 | program.version(version);
|
136 | addCommand([['command', 'r'], ['alias', 'random'], ['description', ['view random snippet']], ...commonOptions, addAction('random')]);
|
137 | addCommand([['command', 's [query]'], ['alias', 'search'], ['description', ['fuzzy search snippets by id']], ...commonOptions, addAction('search')]);
|
138 | addCommand([['command', 't [id]'], ['alias', 'tag'], ['description', ['view snippets by tag']], ...commonOptions, addAction('tag')]);
|
139 | addCommand([['command', 'v [id]'], ['alias', 'view'], ['description', ['view snippet with id']], ...commonOptions, addAction('view')]);
|
140 | addCommand([['command', '* [id]'], ['description', ['view snippet with id']], addAction('view')]);
|
141 | program.on('--help', () => console.log(['', 'Examples:', ' 30s head', ' 30s v head', ' 30s view head', '', ' 30s r', ' 30s random', '', ' 30s s -j flatten', ' 30s search --json flatten', '', ' 30s t -l ic array', ' 30s tag --layout ic array'].join('\n')));
|
142 | program.parse(process.argv);
|
143 |
|
144 | if (process.argv.length <= 2) {
|
145 | program.outputHelp();
|
146 | }
|