-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlib.js
103 lines (83 loc) · 4.06 KB
/
lib.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
'use strict';
const fs = require('fs');
const debug = require('debug')('yawg');
const _ = require('underscore');
const path = require('path');
const defaultOpts = {
delimiter: ' ',
minLength: 12,
maxLength: 25,
minWords: 3,
maxWords: 5,
minWordLength: 1,
maxWordLength: 8,
attempts: 1e4,
};
// Note, this reads a dictionary file with one word on each line into
// an array. It's not very memory efficient. It would be best to use
// this package in command-line scripts or a stand-alone service
// so it doesn't bloat your main application.
const dictionaryFilePath = path.join(__dirname, 'dictionary/first20hours/2014-12-17-google-10000-english-usa.txt');
const words = fs.readFileSync(dictionaryFilePath).toString()/*.replace("\r\n", "\n")*/.split("\n");
function isString(obj) {
return (typeof obj === 'string' || obj instanceof String);
}
function ensureInt(obj, paramName) {
if (!Number.isInteger(obj)) throw new Error(`Parameter [${paramName}] should be an integer, was [${obj}] of type [${typeof (obj)}].`)
}
function ensureString(obj, paramName) {
if (!isString(obj)) throw new Error(`Parameter [${paramName}] should be a string, was [${obj}] of type [${typeof (obj)}].`)
}
function validateOptions(opts) {
const { delimiter, minWords, minLength, minWordLength, attempts } = opts;
ensureString(delimiter, 'delimiter');
ensureInt(minWords, 'minWords');
ensureInt(minLength, 'minLength');
ensureInt(minWordLength, 'minWordLength');
// Not const, these may be manipulated below
ensureInt(opts.maxWords, 'maxWords');
ensureInt(opts.maxLength, 'maxLength');
ensureInt(opts.maxWordLength, 'maxWordLength');
if (minWords < 1) throw new Error('minWords must be greater than zero.');
if (minLength < 1) throw new Error('minWords must be greater than zero.');
if (minWordLength < 1) throw new Error('minWordLength must be greater than zero.');
if (opts.maxWordLength > opts.maxLength) throw new Error('maxWordLength should not be greater than maxLength.');
if (opts.minWordLength * opts.minWords > opts.maxLength) throw new Error(`minWordLength[${opts.minWordLength}] times minWords[${opts.minWords}] will always produce longer phrases than maxLength[${opts.maxLength}].`);
if (opts.maxWords < minWords) { debug('maxWords less than minWords, setting equal to minWords'); opts.maxWords = minWords; }
if (opts.maxLength < minLength) { debug('maxLength less than minLength, setting equal to minLength'); opts.maxLength = minLength; }
if (opts.maxWordLength < minWordLength) { debug('mmaxWordLength less than minWordLength, setting equal to minWordLength'); opts.maxWordLength = minWordLength; }
}
function randomInt(lowInclusive, highExclusive) {
return Math.floor(Math.random() * (highExclusive - lowInclusive) + lowInclusive);
}
function yawg(opts) {
const opts2 = { ...{}, ...defaultOpts, ...opts };
const { delimiter, minWords, maxWords, minLength, maxLength, minWordLength, maxWordLength, attempts } = opts2;
const randomWordCount = randomInt(minWords, maxWords + 1)
const candidateWordsStartIdx = words.findIndex(w => w.length >= minWordLength);
const candidateWordsEndIdx = words.findIndex(w => w.length > maxWordLength) - 1;
debug(`startIdx: ${candidateWordsStartIdx} endIdx: ${candidateWordsEndIdx}`)
const randomWord = () => {
const idx = randomInt(candidateWordsStartIdx, candidateWordsEndIdx + 1);
return words[idx];
}
// Up to N attempts at generating a phrase, to avoid infinite loop
for (let i = 0; i < attempts; i++) {
const chosenWords = _.times(randomWordCount, randomWord);
const phrase = chosenWords.join(delimiter);
const messagePrefix = `Attempt #${(i + 1)}: phrase[${phrase}]`;
if (phrase.length < minLength) {
debug(`${messagePrefix}: Phrase too short.`);
}
else if (phrase.length > maxLength) {
debug(`${messagePrefix}: Phrase too long.`);
} else {
debug(`${messagePrefix}: OK!`);
return phrase;
}
}
throw new Error('Failed to generate phrase within constraints.');
}
yawg.validateOptions = validateOptions;
yawg.defaultOpts = defaultOpts
exports = module.exports = yawg;