UNPKG

6.67 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3'use strict';
4
5function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
7require('core-js/modules/es.array.iterator');
8var _reduce = _interopDefault(require('ramda/src/reduce'));
9var program = _interopDefault(require('commander'));
10require('core-js/modules/es.string.replace');
11var _map = _interopDefault(require('ramda/src/map'));
12var _prop = _interopDefault(require('ramda/src/prop'));
13var _descend = _interopDefault(require('ramda/src/descend'));
14var _sort = _interopDefault(require('ramda/src/sort'));
15var _lensProp = _interopDefault(require('ramda/src/lensProp'));
16var _set = _interopDefault(require('ramda/src/set'));
17var _compose = _interopDefault(require('ramda/src/compose'));
18var _multiply = _interopDefault(require('ramda/src/multiply'));
19var _always = _interopDefault(require('ramda/src/always'));
20var _ifElse = _interopDefault(require('ramda/src/ifElse'));
21var _divide = _interopDefault(require('ramda/src/divide'));
22var _flip = _interopDefault(require('ramda/src/flip'));
23var _any = _interopDefault(require('ramda/src/any'));
24var _filter = _interopDefault(require('ramda/src/filter'));
25var _find = _interopDefault(require('ramda/src/find'));
26var _endsWith = _interopDefault(require('ramda/src/endsWith'));
27var _startsWith = _interopDefault(require('ramda/src/startsWith'));
28var _anyPass = _interopDefault(require('ramda/src/anyPass'));
29var _replace = _interopDefault(require('ramda/src/replace'));
30var _is = _interopDefault(require('ramda/src/is'));
31var snippets = _interopDefault(require('../lib/snippets.json'));
32var _pick = _interopDefault(require('ramda/src/pick'));
33var _toPairs = _interopDefault(require('ramda/src/toPairs'));
34var _join = _interopDefault(require('ramda/src/join'));
35var Renderer = _interopDefault(require('marked-terminal'));
36var chalk = _interopDefault(require('chalk'));
37var marked = _interopDefault(require('marked'));
38var cliHighlight = require('cli-highlight');
39var clipboardy = require('clipboardy');
40
41const enforceSingleNewLine = x => _is(String, x) ? _compose(_replace(/[\r\n]+/g, '\n'), _replace(/[\r\n]*$/, ''))(x) : x;
42const randomNumberInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
43const startsOrEndsWith = (val, query) => _anyPass([_startsWith, _endsWith])(query, val);
44
45const fuzzyMatch = query => x => new RegExp(query).test(x.id);
46
47const getSnippet = (snippets, id) => _find(x => x.id === id, snippets);
48const getSnippetsByTag = (snippets, id) => _filter(x => _any(y => y === id, x.tags), snippets);
49const randomSnippet = snippets => snippets[randomNumberInRange(0, snippets.length - 1)];
50
51const 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
63const setSearchRelevance = query => x => _set(_lensProp('relevance'), computeRelevance(x.id, query), x);
64
65const searchSnippets = (snippets, query) => _compose(_sort(_descend(_prop('relevance'))), _map(setSearchRelevance(query)), _filter(fuzzyMatch(query)))(snippets);
66
67marked.setOptions({
68 renderer: new Renderer()
69});
70const 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};
87const 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
114var version = "1.5.1";
115
116const isTest = process.env.NODE_ENV === 'test';
117const 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
124const addCommand = settings => _reduce((acc, [key, ...args]) => acc[key](...args), program, settings);
125
126const addAction = action => ['action', (...args) => ((input, opts = {}) => {
127 if (!input) return program.outputHelp();
128 return isTest ? // NOTE: Commander.js sadly does not include a way to hook onto given
129 // actions for integration testing. We solve this by outputting state
130 // when NODE_ENV is test.
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
134const 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']];
135program.version(version);
136addCommand([['command', 'r'], ['alias', 'random'], ['description', ['view random snippet']], ...commonOptions, addAction('random')]);
137addCommand([['command', 's [query]'], ['alias', 'search'], ['description', ['fuzzy search snippets by id']], ...commonOptions, addAction('search')]);
138addCommand([['command', 't [id]'], ['alias', 'tag'], ['description', ['view snippets by tag']], ...commonOptions, addAction('tag')]);
139addCommand([['command', 'v [id]'], ['alias', 'view'], ['description', ['view snippet with id']], ...commonOptions, addAction('view')]);
140addCommand([['command', '* [id]'], ['description', ['view snippet with id']], addAction('view')]);
141program.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')));
142program.parse(process.argv);
143
144if (process.argv.length <= 2) {
145 program.outputHelp();
146}