├── .babelrc ├── .gitignore ├── README.md ├── circle.yml ├── package.json ├── src └── ja-yahoo-kousei.js └── test ├── mocha.opts └── yahoo-kousei-test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | lib 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # textlint-rule-ja-yahoo-kousei 2 | [textlint](https://github.com/textlint/textlint) rule that using [Yahoo Proofreading API](http://developer.yahoo.co.jp/webapi/jlp/kousei/v1/kousei.html). 3 | 4 | ## Installation 5 | 6 | ``` 7 | $ npm install textlint-rule-ja-yahoo-kousei 8 | ``` 9 | 10 | ## Usage 11 | ``` 12 | $ npm install -g textlint textlint-rule-ja-yahoo-kousei 13 | ``` 14 | 15 | Should get Yahoo Application ID [here](https://e.developer.yahoo.co.jp/dashboard/). 16 | And set YAHOO_APP_ID. 17 | 18 | ``` 19 | $ export YAHOO_APP_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 20 | ``` 21 | 22 | or set in `.textlintrc`. 23 | 24 | ``` 25 | { 26 | "rules": { 27 | "ja-yahoo-kousei": { 28 | 'appID': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' 29 | } 30 | } 31 | } 32 | ``` 33 | 34 | run. 35 | 36 | ``` 37 | $ textlint --rule textlint-rule-ja-yahoo-kousei README.md 38 | ``` 39 | 40 | ## Config 41 | 42 | You can set words that ignore. 43 | 44 | ``` 45 | { 46 | "rules": { 47 | "ja-yahoo-kousei": { 48 | 'ignores': { 49 | '用字':['彼方'] 50 | } 51 | } 52 | } 53 | } 54 | ``` 55 | 56 | or use wildcard; 57 | 58 | ``` 59 | { 60 | "rules": { 61 | "ja-yahoo-kousei": { 62 | 'ignores': { 63 | '用字': '*' 64 | } 65 | } 66 | } 67 | } 68 | ``` 69 | 70 | ## Example 71 | TODO 72 | 73 | ## Tests 74 | 75 | ``` 76 | $ export YAHOO_APP_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 77 | $ npm test 78 | ``` 79 | 80 | ## License 81 | MIT 82 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 4.2 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "textlint-rule-ja-yahoo-kousei", 3 | "repository": { 4 | "type": "git", 5 | "url": "https://github.com/KeitaMoromizato/textlint-rule-ja-yahoo-kousei.git" 6 | }, 7 | "version": "1.0.3", 8 | "description": "textlint rule that using Yahoo Proofreading API", 9 | "scripts": { 10 | "build": "babel src --out-dir lib --source-maps", 11 | "watch": "babel src --out-dir lib --watch --source-maps", 12 | "prepublish": "npm run --if-present build", 13 | "test": "mocha" 14 | }, 15 | "main": "lib/ja-yahoo-kousei.js", 16 | "files": [ 17 | "lib", 18 | "src" 19 | ], 20 | "keywords": [ 21 | "textlint" 22 | ], 23 | "bugs": { 24 | "url": "https://github.com/KeitaMoromizato/textlint-rule-ja-yahoo-kousei/issues" 25 | }, 26 | "author": "Keita Moromizato", 27 | "email": "keita_moromizato@relationsgroup.co.jp", 28 | "license": "MIT", 29 | "devDependencies": { 30 | "babel-cli": "^6.8.0", 31 | "babel-preset-es2015": "^6.6.0", 32 | "espower-babel": "^4.0.3", 33 | "mocha": "^2.3.4", 34 | "power-assert": "^1.4.0", 35 | "textlint": "^6.6.0" 36 | }, 37 | "dependencies": { 38 | "lodash": "^4.11.2", 39 | "request": "^2.67.0", 40 | "xml2js": "^0.4.15" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/ja-yahoo-kousei.js: -------------------------------------------------------------------------------- 1 | import assign from 'lodash/assign'; 2 | import find from 'lodash/find'; 3 | 4 | import request from 'request'; 5 | import {parseString} from 'xml2js'; 6 | 7 | const defaultOptions = { 8 | ignores:{} 9 | }; 10 | 11 | function fetchProofreading(appID, text) { 12 | return new Promise((resolve, reject) => { 13 | request.post({ 14 | url: 'http://jlp.yahooapis.jp/KouseiService/V1/kousei', 15 | form: { 16 | appid: appID, 17 | sentence: text 18 | }, 19 | json: true 20 | }, (error, response, body) => { 21 | if (error) return reject(error); 22 | 23 | parseString(body, (error, result) => { 24 | if (error) return reject(error); 25 | 26 | resolve(result); 27 | }); 28 | }); 29 | }); 30 | } 31 | 32 | function getReportMessage(record, lang = 'en') { 33 | const info = record.ShitekiInfo[0]; 34 | const invalidWord = record.Surface[0]; 35 | const validWord = record.ShitekiWord.length ? `(${record.ShitekiWord[0]})` : ''; 36 | 37 | switch(lang) { 38 | case 'ja': 39 | return `「${invalidWord}」は${info}です。${validWord}`; 40 | default: 41 | return `${invalidWord} => ${info}${validWord}` 42 | } 43 | } 44 | 45 | function containedIgnoreList(word, info, ignores) { 46 | const target = ignores[info]; 47 | 48 | if (!target) { 49 | return false; 50 | } 51 | else if (target === '*') { 52 | return true; 53 | } 54 | else if (Array.isArray(target)) { 55 | return !!find(target, w => w === word); 56 | } 57 | 58 | return false; 59 | } 60 | 61 | export default function(context, options = {}) { 62 | const appID = options.appID || process.env.YAHOO_APP_ID; 63 | 64 | if (!appID) { 65 | throw new Error(`YAHOO_APP_ID is not set.`); 66 | } 67 | 68 | options = assign({}, defaultOptions, options); 69 | const {Syntax, getSource, report, RuleError} = context; 70 | 71 | return { 72 | [Syntax.Paragraph](node) { 73 | return new Promise((resolve, reject) => { 74 | const paragraph = getSource(node); 75 | 76 | fetchProofreading(appID, paragraph).then(json => { 77 | if (json.Error) { 78 | return reject(new Error(json.Error.Message[0])); 79 | } 80 | 81 | if (json && json.ResultSet.Result) { 82 | json.ResultSet.Result.forEach(result => { 83 | 84 | if (containedIgnoreList(result.Surface[0], result.ShitekiInfo[0], options.ignores)) return; 85 | 86 | report(node, new RuleError(getReportMessage(result, options.lang))); 87 | }); 88 | } 89 | 90 | resolve(); 91 | }).catch(error => { 92 | reject(error); 93 | }); 94 | }); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --compilers js:espower-babel/guess 2 | -------------------------------------------------------------------------------- /test/yahoo-kousei-test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import assert from 'power-assert'; 4 | import {TextLintCore} from 'textlint'; 5 | import rule from '../src/ja-yahoo-kousei.js'; 6 | 7 | describe('yahoo-kousei', function() { 8 | 9 | context('when use sample text', () => { 10 | const textlint = new TextLintCore(); 11 | 12 | textlint.setupRules({ 13 | 'ja-yahoo-kousei': rule 14 | }); 15 | 16 | it('should report error', (done) => { 17 | // sample text from 18 | // http://developer.yahoo.co.jp/webapi/jlp/kousei/v1/kousei.html 19 | textlint.lintMarkdown('遙か彼方に小形飛行機が見える。').then(result => { 20 | assert(result.messages.length === 3); 21 | }).then(done, done); 22 | }); 23 | }); 24 | 25 | context('when use filter config', () => { 26 | 27 | it('should report filtered error', (done) => { 28 | 29 | const textlint = new TextLintCore(); 30 | 31 | textlint.setupRules({ 32 | 'ja-yahoo-kousei': rule 33 | }, { 34 | 'ja-yahoo-kousei': { 35 | 'ignores': { 36 | '用字':['彼方'] 37 | } 38 | } 39 | }); 40 | 41 | // sample text from 42 | // http://developer.yahoo.co.jp/webapi/jlp/kousei/v1/kousei.html 43 | textlint.lintMarkdown('遙か彼方に小形飛行機が見える。').then(result => { 44 | assert(result.messages.length === 2); 45 | }).then(done, done); 46 | }); 47 | 48 | it('should report filtered error(use wildcard)', (done) => { 49 | 50 | const textlint = new TextLintCore(); 51 | 52 | textlint.setupRules({ 53 | 'ja-yahoo-kousei': rule 54 | }, { 55 | 'ja-yahoo-kousei': { 56 | 'ignores': { 57 | '用字': '*' 58 | } 59 | } 60 | }); 61 | 62 | // sample text from 63 | // http://developer.yahoo.co.jp/webapi/jlp/kousei/v1/kousei.html 64 | textlint.lintMarkdown('遙か彼方に小形飛行機が見える。').then(result => { 65 | assert(result.messages.length === 2); 66 | }).then(done, done); 67 | }); 68 | }); 69 | 70 | context('when use language config', () => { 71 | const textlint = new TextLintCore(); 72 | 73 | textlint.setupRules({ 74 | 'ja-yahoo-kousei': rule 75 | }, { 76 | 'ja-yahoo-kousei': { 77 | 'ignores': { 78 | '用字':['彼方'], 79 | '表外漢字あり': ['遙か'] 80 | }, 81 | 'lang': 'ja' 82 | } 83 | }); 84 | 85 | it('should report filtered error', (done) => { 86 | 87 | textlint.lintMarkdown('遙か彼方に小形飛行機が見える。').then(result => { 88 | assert(result.messages.length === 1); 89 | assert(result.messages[0].message == '「小形飛行機」は誤変換です。(小型飛行機)'); 90 | }).then(done, done); 91 | }); 92 | }); 93 | }); 94 | --------------------------------------------------------------------------------