Socket
Socket
Sign inDemoInstall

yaspeller

Package Overview
Dependencies
Maintainers
1
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

yaspeller - npm Package Compare versions

Comparing version 0.1.3 to 0.3.1

.jscs.json

16

.yaspeller.dictionary.json
[
"бандл",
"десктопных",
"десктопной",
"закоммиченное",
"конфига",
"маппинга",
"минимизатором",
"шаблонизатор",
"шаблонизаторы",
"разработческий",
"рендеринга",
"чекаута",
"чекаутом"
]
"масква"
]
#!/usr/bin/env node
var fs = require('fs'),
pth = require('path'),
request = require('request'),
chalk = require('chalk'),
isutf8 = require('isutf8'),
Q = require('q'),
program = require('commander'),
yaspeller = require('../lib/yaspeller'),
program = require('commander'),
xml2js = require('xml2js'),
printDebug = require('../lib/print_debug'),
FILENAME_DICTIONARY = '.yaspeller.dictionary.json',
FILE_EXTENSIONS = ['wiki', 'md', 'txt', 'text', 'html', 'htm', 'json', 'js', 'css', 'xml', 'svg'],
isDir = function(path) {
return fs.statSync(path).isDirectory();
},
dictionary = [];
dictionary;
if(fs.existsSync(FILENAME_DICTIONARY)) {
dictionary = JSON.parse(fs.readFileSync(FILENAME_DICTIONARY, 'utf-8'));
}
function getDictionary(filename) {
var dict;
function printErrors(resource, words) {
var bufWords = delDuplicates(words);
printDebug('get/check dictionary: ' + filename);
if(fs.existsSync(filename)) {
try {
dict = fs.readFileSync(filename);
if(!isutf8(dict)) {
console.error(filename + ': is not utf-8');
process.exit(1);
}
bufWords = markRuEnSymbols(bufWords);
if(bufWords.length) {
console.log('Resource: ' + resource);
console.log('typos: ' + bufWords.join(', ') + '\n');
dictionary = JSON.parse(dict.toString('utf-8'));
printDebug('use dictionary: ' + filename);
} catch(e) {
console.error(filename + ': error parsing JSON');
process.exit(1);
}
}
}
function firstUpperCase(word) {
return word.substr(0, 1).toUpperCase() + word.substr(1);
return dict || [];
}
function delDuplicates(ar) {
var props = {},
result = [];
function getTypos(data) {
var buf = [];
data.forEach(function(el) {
var find = false;
// ERROR_UNKNOWN_WORD: Слова нет в словаре
if(el.code === 1) {
dictionary.some(function(el2) {
if(el2 === el.word) {
find = true;
}
ar.forEach(function(el) {
if(props[el]) {
props[el]++;
} else {
props[el] = 1;
return find;
});
}
if(!find) {
buf.push(el.word);
}
});
Object.keys(props).forEach(function(key) {
if(props[key] > 1) {
result.push(key + ' (' + props[key] + ')');
} else {
result.push(key);
var obj = {};
buf.forEach(function(word) {
if(!obj[word]) {
obj[word] = {
count: 0,
comment: []
};
if(word.search(/[a-z]/i) > -1 && word.search(/[а-яё]/i) > -1) {
obj[word].comment = [
chalk.red('en: ' + word.replace(/[а-яё]/gi, '*')),
chalk.green('ru: ' + word.replace(/[a-z]/gi, '*'))
];
}
}
obj[word].count++;
});
return result.sort();
}
var typos = [];
Object.keys(obj).forEach(function(w) {
var comment = [],
item = obj[w];
if(item.count > 1) {
comment.push(chalk.cyan('count: ' + item.count));
}
function markRuEnSymbols(words) {
var result = [];
words.forEach(function(el) {
if(el.search(/[a-z]/i) !== -1 && el.search(/[а-яё]/i) !== -1) {
result.push(el + ' (en: ' + el.replace(/[а-яё]/gi, '*') + ', ru: ' + el.replace(/[a-z]/gi, '*') + ')');
} else {
result.push(el);
if(item.comment.length) {
comment = comment.concat(item.comment);
}
typos.push(w + (comment.length ? ' (' + comment.join(', ') + ')' : ''));
});
return result;
return typos;
}
function getWords(data) {
var res = [];
function getRepeatWords(data) {
var words = [];
data.forEach(function(el) {
var word = firstUpperCase(el.word);
var find = false;
dictionary.forEach(function(el2) {
var dword = firstUpperCase(el2);
if(dword === word) {
find = true;
}
});
if(!find) {
res.push(el.word);
// ERROR_REPEAT_WORD: Повтор слова
if(el.code === 2) {
words.push(el.word);
}
});
return res;
return words;
}
function checkText(text, resource, options) {
// Если в тексте нет русских символов, то проверять не нужно
if(!text || (options.lang === 'ru' && text.search(/[а-яё]/i) === -1)) {
return;
}
yaspeller.checkText(text, options.lang, options.options, options.format, function(error, data) {
var words = getWords(data);
printErrors(resource, words);
function getCapitalisation(data) {
var words = [];
data.forEach(function(el) {
// ERROR_CAPITALIZATION: Неверное употребление прописных и строчных букв
if(el.code === 3) {
words.push(el.word);
}
});
}
function checkFile(file, options) {
var text = fs.readFileSync(file, 'utf-8');
checkText(text, file, options);
return words;
}
function checkUrl(url, options) {
request.get(url, function(error, response, text) {
checkText(text, url, options);
function hasManyErrors(data) {
var hasErrors = false;
data.some(function(el) {
// ERROR_TOO_MANY_ERRORS: Текст содержит слишком много ошибок
if(el.code === 4) {
hasErrors = true;
return true;
}
return false;
});
return hasErrors;
}
function checkDir(dir, options) {
var files = findFiles(Array.isArray(dir) ? dir : [dir]);
files.forEach(function(file) {
checkFile(file, options);
});
function getTextError(title, words) {
var SEPARATOR = '\n-----';
return chalk.cyan(title + ': ' + words.length + SEPARATOR + '\n') + words.join('\n') + chalk.cyan(SEPARATOR);
}
function findFiles(files) {
var res = [],
regExp = new RegExp('\.(' + FILE_EXTENSIONS.join('|') + ')$', 'i'),
find = function (path) {
var files = fs.readdirSync(path);
files.forEach(function (el) {
var file = pth.join(path, el);
if (isDir(file)) {
find(file);
} else if (file.search(regExp) !== -1) {
res.push(file);
}
});
};
function buildResource(err, data) {
if(err) {
console.error(chalk.red(data));
} else {
var typos = getTypos(data.data),
repeatWords = getRepeatWords(data.data),
capitalization = getCapitalisation(data.data),
textErrors = [];
files.forEach(function (el) {
if(isDir(el)) {
find(el);
} else {
res.push(el);
if(hasManyErrors(data.data)) {
textErrors.push(chalk.red('Too many errors'));
}
});
return res;
}
if(repeatWords.length) {
textErrors.push(getTextError('Repeat words', repeatWords));
}
function checkSitemap(url, options) {
request.get(url, function(error, response, xml) {
var parser = new xml2js.Parser();
parser.parseString(xml, function (err, result) {
result.urlset.url.forEach(function(el) {
el.loc.forEach(function(url) {
checkUrl(url, options);
});
});
});
});
if(capitalization.length) {
textErrors.push(getTextError('Capitalization', capitalization));
}
if(typos.length) {
textErrors.push(getTextError('Typos', typos));
}
if(textErrors.length) {
console.error(chalk.red.bold('[ERR]'), data.resource);
console.error(textErrors.join('\n') + '\n');
} else {
console.log(chalk.bold.green('[OK]'), data.resource);
}
}
}
program
.version(JSON.parse(fs.readFileSync(__dirname + '/../package.json')).version)
.usage('[options] <file-or-directory-or-link>')
.version(require('../package.json').version)
.usage('[options] <file-or-directory-or-link...>')
.option('-l, --lang <s>', 'Langs: ru, en, tr. Default: "en,ru"')
.option('-d, --debug', 'Debug mode')
.option('-di, --dictionary <s>', 'json file for own dictionary')
.option('-f, --format <s>', 'Formats: plain or html. Default: plain')
.parse(process.argv);
var resource = process.argv[2],
timeA = Date.now(),
opt = {format: 'html', lang: 'ru'};
var startTime = Date.now(),
settings = {};
if(!resource) {
if(program.lang) {
settings.lang = program.lang;
}
if(program.format) {
settings.format = program.format;
}
if(!program.args.length) {
program.help();
}
if(resource.search(/^https?:/) !== -1) {
if(resource.search(/sitemap\.xml$/) !== -1) {
checkSitemap(resource, opt);
} else {
checkUrl(resource, opt);
}
} else {
if(fs.existsSync(resource)) {
if(isDir(resource)) {
checkDir(resource, opt);
var hasErrors = false,
onNext = function(data) {
data.forEach(function(el) {
if(el[0]) {
hasErrors = true;
}
buildResource(el[0], el[1]);
});
};
yaspeller.setDebug(program.debug);
dictionary = getDictionary(program.dictionary || FILENAME_DICTIONARY);
var queries = [];
program.args.forEach(function(resource) {
queries.push(Q.Promise(function(resolve) {
if(resource.search(/^https?:/) > -1) {
if(resource.search(/sitemap\.xml$/) > -1) {
yaspeller.checkSitemap(resource, function(err, data) {
onNext(err, data);
resolve();
}, settings);
} else {
yaspeller.checkUrl(resource, function(err, data) {
onNext([[err, data]]);
resolve();
}, settings);
}
} else {
checkFile(resource, opt);
if(fs.existsSync(resource)) {
if(fs.statSync(resource).isDirectory()) {
yaspeller.checkDir(resource, function(err, data) {
onNext(err, data);
resolve();
}, settings);
} else {
yaspeller.checkFile(resource, function(err, data) {
onNext([[err, data]]);
resolve();
}, settings);
}
} else {
onNext([[true, Error(resource + ': is not exists')]]);
resolve();
}
}
} else {
console.log(resource + ': No such file or directory');
}
}
}));
});
Q.all(queries).then(function() {
console.log(chalk.magenta('Build finished: ' + ((+new Date() - startTime) / 1000) + ' sec.'));
process.exit(hasErrors ? 1 : 0);
});

@@ -1,20 +0,69 @@

var http = require('http'),
qs = require('querystring'),
request = require('request');
/* jshint maxlen: 300 */
var request = require('request'),
pth = require('path'),
fs = require('fs'),
isutf8 = require('isutf8'),
Q = require('q'),
printDebug = require('./print_debug'),
xml2js = require('xml2js'),
settings = null,
isDebug = false,
fileExtensions;
var FILENAME_SETTINGS = 'yaspeller.json',
YASPELLER_API_URL = 'http://speller.yandex.net/services/spellservice.json/checkText';
function getExtension(file) {
var buf = file.split('.');
return buf[buf.length - 1];
}
function isDir(dir) {
return fs.statSync(dir).isDirectory();
}
function getFormat(format, ext) {
if(!format || format === 'auto') {
var exts = getSettings().fileExtensions;
if(exts[ext] && exts[ext].format) {
return exts[ext].format;
} else {
return 'plain';
}
}
return format;
}
function getSettings() {
if(!settings) {
settings = JSON.parse(fs.readFileSync(FILENAME_SETTINGS));
}
return settings;
}
/*function getFileExtensions() {
if(!fileExtensions) {
fileExtensions = Object.keys(getSettings().fileExtensions);
}
return fileExtensions;
}*/
function getOptions(options) {
var result = 0,
standartOptions = {
IGNORE_UPPERCASE: 1, // Пропускать слова, написанные заглавными буквами, например, "ВПК"
IGNORE_DIGITS: 2, //Пропускать слова с цифрами, например, "авп17х4534"
IGNORE_URLS: 4, // Пропускать интернет-адреса, почтовые адреса и имена файлов
FIND_REPEAT_WORDS: 8, // Подсвечивать повторы слов, идущие подряд. Например, "я полетел на на Кипр"
IGNORE_LATIN: 16, // Пропускать слова, написанные латиницей, например, "madrid"
NO_SUGGEST: 32, // Только проверять текст, не выдавая вариантов для замены
FLAG_LATIN: 128, // Отмечать слова, написанные латиницей, как ошибочные
BY_WORDS: 256, // Не использовать словарное окружение (контекст) при проверке. Опция полезна в случаях, когда на вход сервиса передается список отдельных слов
IGNORE_CAPITALIZATION: 512, // Игнорировать неверное употребление ПРОПИСНЫХ/строчных букв, например, в слове "москва"
IGNORE_ROMAN_NUMERALS: 2048 // Игнорировать римские цифры ("I, II, III, ...")
};
IGNORE_UPPERCASE: 1,
IGNORE_DIGITS: 2,
IGNORE_URLS: 4,
FIND_REPEAT_WORDS: 8,
IGNORE_LATIN: 16,
NO_SUGGEST: 32,
FLAG_LATIN: 128,
BY_WORDS: 256,
IGNORE_CAPITALIZATION: 512,
IGNORE_ROMAN_NUMERALS: 2048
};
Object.keys(options || {}).forEach(function(key) {

@@ -25,3 +74,3 @@ if(standartOptions[key] && options[key]) {

});
return result;

@@ -31,29 +80,85 @@ }

function prepareText(text, format) {
text = text.replace(/<\/?[^>]+>/g, ' ');
text = text.replace(/\s+/g, ' ');
text = ('' + text).trim();
return text;
var buf = text.trim();
if(format === 'html') {
buf = buf.replace(/<\/?[^>]+>/g, ' '); // strip html tags
}
return buf.replace(/\r\n/g, '\n') // fix Windows
.replace(/\r/g, '\n') // fix MacOS
.replace(/\s+\n/g, '\n') // trailling spaces
.replace(/\s+/g, ' ') // repeat spaces
.replace(/\n+/g, '\n')
.trim();
}
function checkText(text, lang, options, format, callback) {
var bufText;
function findFiles(dir) {
var res = [],
regExp = new RegExp('\\.(' + fileExtensions.join('|') + ')$', 'i'),
find = function(path) {
var files = fs.readdirSync(path);
files.forEach(function(el) {
var file = pth.join(path, el);
if(isDir(file)) {
find(file);
} else if(file.search(regExp) !== -1) {
res.push(file);
}
});
};
if(isDir(dir)) {
find(dir);
} else {
res.push(dir);
}
return res;
}
/**
* Check text for typos
*
* @param {string} text
* @param {Function} callback
* @tutorial settings
* @param {Object} [settings] Настройки
* @param {string} [settings.format] Формат текста: plain или html
* @param {string|Array} [settings.lang] Языки проверки: ru – русский, uk – украинский, en – английский
* @param {Object} [settings.options] Опции
* @param {boolean} [settings.options.IGNORE_UPPERCASE] Пропускать слова, написанные заглавными буквами, например, "ВПК"
* @param {boolean} [settings.options.IGNORE_DIGITS] Пропускать слова с цифрами, например, "авп17х4534"
* @param {boolean} [settings.options.IGNORE_URLS] Пропускать интернет-адреса, почтовые адреса и имена файлов
* @param {boolean} [settings.options.FIND_REPEAT_WORDS] Подсвечивать повторы слов, идущие подряд. Например, "я полетел на на Кипр"
* @param {boolean} [settings.options.IGNORE_LATIN] Пропускать слова, написанные латиницей, например, "madrid"
* @param {boolean} [settings.options.NO_SUGGEST] Только проверять текст, не выдавая вариантов для замены
* @param {boolean} [settings.options.FLAG_LATIN] Отмечать слова, написанные латиницей, как ошибочные
* @param {boolean} [settings.options.BY_WORDS] Не использовать словарное окружение (контекст) при проверке. Опция полезна в случаях, когда на вход сервиса передается список отдельных слов
* @param {boolean} [settings.options.IGNORE_CAPITALIZATION] Игнорировать неверное употребление ПРОПИСНЫХ/строчных букв, например, в слове "москва"
* @param {boolean} [settings.options.IGNORE_ROMAN_NUMERALS] Игнорировать римские цифры ("I, II, III, ...")
*/
function checkText(text, callback, settings) {
settings = settings || {};
var bufText,
format = settings.format,
options = settings.options,
lang = settings.lang;
if(Array.isArray(text)) {
bufText = [];
text.forEach(function(el) {
var t = prepareText(text, format);
if(t) {
bufText.push(t);
}
bufText.push(prepareText(el, format));
});
bufText = bufText.join('\n');
} else {
bufText = prepareText(text, format);
}
if(Array.isArray(lang)) {
lang = lang.join(',');
}
request.post('http://speller.yandex.net/services/spellservice.json/checkText', {
request.post(YASPELLER_API_URL, {
form: {

@@ -65,7 +170,7 @@ format: format || 'plain',

}
}, function (error, response, body) {
}, function(error, response, body) {
if(!error && response.statusCode === 200) {
callback(false, JSON.parse(body));
} else {
callback(true, response.statusCode);
callback(true, Error('API returns status code is ' + response.statusCode));
}

@@ -75,4 +180,206 @@ });

/**
* Check text in file on typos
*
* @param {string} file
* @param {Function} callback
* @param {Object} [settings] See {@tutorial options}
*/
function checkFile(file, callback, settings) {
settings = settings || {};
if(isDebug) {
printDebug('get: ' + file);
}
if(fs.existsSync(file)) {
if(fs.statSync(file).isFile()) {
var buf = fs.readFileSync(file);
if(isutf8(buf)) {
settings.format = getFormat(settings.format, getExtension(file, settings.format));
if(isDebug) {
printDebug('post text -> api: ' + file);
}
checkText(buf.toString(), function(err, data) {
callback(err, !err ? {resource: file, data: data} : data);
}, settings);
} else {
callback(true, Error(file + ': is not utf-8'));
}
} else {
callback(true, Error(file + ': is not file'));
}
} else {
callback(true, Error(file + ': is not exists'));
}
}
/**
* Check text on link for typos
*
* @param {string} url
* @param {Function} callback
* @param {Object} [settings] See {@tutorial settings}
*/
function checkUrl(url, callback, settings) {
if(isDebug) {
printDebug('get: ' + url);
}
request.get(url, function(error, response, text) {
if(error || response.statusCode !== 200) {
callback(true, Error(url + ': returns status code is ' + response.statusCode));
return;
}
checkText(text, function(err, data) {
callback(err, {resource: url, data: data});
}, settings);
});
}
/**
* Check text in files in folders on typos
*
* @param {string} dir
* @param {Function} callback
* @param {Object} [settings] See {@tutorial settings}
*/
function checkDir(dir, callback, settings) {
if(!fs.existsSync(dir)) {
callback([[true, Error(dir + ': file or directory is not exists')]]);
return;
}
var files = findFiles(dir),
queries = [];
files.forEach(function(file) {
queries.push(Q.Promise(function(resolve) {
checkFile(file, function(err, data) {
resolve([err, data]);
}, settings);
}));
});
Q.all(queries).done(function(data) {
callback(data);
});
}
/**
* Check text on pages of sitemap.xml
*
* @param {string} url
* @param {Function} callback
* @param {Object} [settings] See {@tutorial settings}
*/
function checkSitemap(url, callback, settings) {
var queries = [],
results = [];
if(isDebug) {
printDebug('get: ' + url);
}
request.get(url, function(error, response, xml) {
if(error || response.statusCode !== 200) {
results.push([true, Error(url + ': returns status code is ' + response.statusCode)]);
callback(results);
return;
}
var parser = new xml2js.Parser();
parser.parseString(xml, function(err, result) {
if(err) {
results.push([true, Error(url + ': error parsing xml')]);
callback(results);
return;
}
if(result && result.urlset && Array.isArray(result.urlset.url)) {
result.urlset.url.forEach(function(el) {
el.loc.forEach(function(url) {
queries.push(Q.Promise(function(resolve) {
checkUrl(url, function(err, data) {
resolve([err, data]);
}, settings);
}));
});
});
}
Q.all(queries).done(function(data) {
callback(data);
});
});
});
}
/**
* Check text on pages of sitemap.xml
*
* @param {string} url
* @param {Function} callback
* @param {Object} [settings] See {@tutorial settings}
*/
function checkSitemap(url, callback, settings) {
var queries = [],
results = [];
if(isDebug) {
printDebug('get: ' + url);
}
request.get(url, function(error, response, xml) {
if(error || response.statusCode !== 200) {
results.push([true, Error(url + ': returns status code is ' + response.statusCode)]);
callback(results);
return;
}
var parser = new xml2js.Parser();
parser.parseString(xml, function(err, result) {
if(err) {
results.push([true, result]);
callback(results);
return;
}
if(result && result.urlset && Array.isArray(result.urlset.url)) {
result.urlset.url.forEach(function(el) {
el.loc.forEach(function(url) {
queries.push(Q.Promise(function(resolve) {
checkUrl(url, function(err, data) {
resolve([err, data]);
}, settings);
}));
});
});
}
Q.all(queries).done(function(data) {
callback(data);
});
});
});
}
module.exports = {
checkText: checkText
setDebug: function(val) {
isDebug = val;
},
setFileExtensions: function(exts) {
fileExtensions = exts;
},
getSettings: getSettings,
checkText: checkText,
checkFile: checkFile,
checkDir: checkDir,
checkUrl: checkUrl,
checkSitemap: checkSitemap
};
{
"author": {
"name": "Denis Seleznev",
"email": "hcodes@yandex.ru",
"url": "https://github.com/hcodes"
},
"name": "yaspeller",
"main": "index.js",
"description": "Проверка опечаток в текстах, в файлах и на сайтах",
"version": "0.1.3",
"homepage": "https://github.com/hcodes/yaspeller",
"repository": {
"type": "git",
"url": "git://github.com/hcodes/yaspeller.git"
},
"keywords": [
"typos", "text", "опечатки", "текст", "Яндекс.Спеллер", "Yandex.Speller"
],
"dependencies": {
"commander": "~1.3",
"request": "~2.47",
"xml2js": "~0.4"
},
"devDependencies": {},
"optionalDependencies": {},
"engines": {
"node": "*"
},
"bin": {
"yaspeller": "./bin/cli.js"
}
"author": {
"name": "Denis Seleznev",
"email": "hcodes@yandex.ru",
"url": "https://github.com/hcodes"
},
"name": "yaspeller",
"main": "index.js",
"description": "Проверка опечаток в текстах, в файлах и на сайтах",
"version": "0.3.1",
"license": "MIT",
"homepage": "https://github.com/hcodes/yaspeller",
"repository": {
"type": "git",
"url": "git://github.com/hcodes/yaspeller.git"
},
"bugs": {
"url": "https://github.com/hcodes/yaspeller/issues"
},
"keywords": [
"typos", "text", "опечатки", "текст", "Яндекс.Спеллер", "Yandex.Speller"
],
"dependencies": {
"commander": "~2.5",
"request": "~2.x",
"isutf8": "~1.0",
"xml2js": "~0.4",
"chalk": "0.5.1",
"q": "~1.1.2"
},
"devDependencies": {
"mocha": "2.0.x",
"chai": "1.x",
"istanbul": "0.3.x",
"jscs": "1.8.x",
"jshint": "2.x"
},
"optionalDependencies": {},
"engines": {
"node": "*"
},
"scripts": {
"test": "npm run-script jshint && npm run-script check-style && npm run-script unit-test-coverage",
"jscs": "./node_modules/.bin/jscs .",
"jshint": "./node_modules/.bin/jshint .",
"unit-test": "./node_modules/.bin/mocha -u bdd -R spec --recursive test",
"unit-test-coverage": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- -u bdd -R spec --recursive test"
},
"bin": {
"yaspeller": "./bin/cli.js"
}
}
yaspeller
=========
[![NPM version](https://badge.fury.io/js/yaspeller.svg)](http://badge.fury.io/js/yaspeller)
[![Build Status](https://travis-ci.org/hcodes/yaspeller.png?branch=master)](https://travis-ci.org/hcodes/yaspeller)
[![Coverage Status](https://coveralls.io/repos/hcodes/yaspeller/badge.png?branch=master)](https://coveralls.io/r/hcodes/yaspeller)
[![Dependency Status](https://gemnasium.com/hcodes/yaspeller.svg)](https://gemnasium.com/hcodes/yaspeller)
Средство поиска опечаток в текстах, в файлах и на сайтах.

@@ -4,0 +9,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc