├── .editorconfig ├── .gitattributes ├── .github ├── renovate.json └── workflows │ ├── ci.yml │ └── npm-publish.yml ├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── esbuild.config.ts ├── package-lock.json ├── package.json ├── src ├── command.ts ├── index.ts ├── parser.ts ├── types.ts └── util.ts ├── test ├── index.cjs └── index.mjs ├── tsconfig.json ├── tsconfig.types.json └── xo.config.cjs /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.json] 13 | indent_style = space 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["github>enjoyjs/renovate-config"] 3 | } 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_call: 9 | 10 | jobs: 11 | test: 12 | name: Test 13 | 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | node-version: 20 | - current 21 | - lts/* 22 | - lts/-1 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | - name: Install MeCab 27 | run: | 28 | sudo apt-get update 29 | sudo apt-get install -y --no-install-recommends mecab 30 | mecab --version 31 | - name: Use Node.js ${{ matrix.node-version }} 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: ${{ matrix.node-version }} 35 | cache: npm 36 | - run: npm ci 37 | - run: npm run build --if-present 38 | - run: npm test 39 | 40 | lint: 41 | name: Lint 42 | 43 | runs-on: ubuntu-latest 44 | 45 | steps: 46 | - uses: actions/checkout@v4 47 | - name: Use Node.js LTS 48 | uses: actions/setup-node@v4 49 | with: 50 | node-version: lts/* 51 | cache: npm 52 | - run: npm ci 53 | - run: npm run lint --if-present 54 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: 9 | - created 10 | 11 | jobs: 12 | ci: 13 | uses: ./.github/workflows/ci.yml 14 | 15 | build: 16 | needs: ci 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: lts/* 23 | - run: npm ci 24 | - run: npm run build 25 | - name: Upload a Build Artifact 26 | uses: actions/upload-artifact@v3 27 | with: 28 | name: lib 29 | path: lib/ 30 | 31 | publish-npm: 32 | needs: build 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v4 36 | - uses: actions/setup-node@v4 37 | with: 38 | node-version: lts/* 39 | registry-url: https://registry.npmjs.org/ 40 | - name: Download a Build Artifact 41 | uses: actions/download-artifact@v3 42 | with: 43 | name: lib 44 | path: lib/ 45 | - run: npm publish --access public 46 | env: 47 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 48 | 49 | publish-gpr: 50 | needs: build 51 | runs-on: ubuntu-latest 52 | steps: 53 | - uses: actions/checkout@v4 54 | - uses: actions/setup-node@v4 55 | with: 56 | node-version: lts/* 57 | registry-url: https://npm.pkg.github.com/ 58 | - name: Download a Build Artifact 59 | uses: actions/download-artifact@v3 60 | with: 61 | name: lib 62 | path: lib/ 63 | - run: npm publish 64 | env: 65 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # parcel-bundler cache (https://parceljs.org/) 61 | .cache 62 | 63 | # next.js build output 64 | .next 65 | 66 | # nuxt.js build output 67 | .nuxt 68 | 69 | # vuepress build output 70 | .vuepress/dist 71 | 72 | # Serverless directories 73 | .serverless 74 | 75 | # FuseBox cache 76 | .fusebox/ 77 | 78 | lib/ 79 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "xo.format.enable": true, 3 | "[typescript]": { 4 | "editor.formatOnPaste": true, 5 | "editor.formatOnSave": true, 6 | "editor.defaultFormatter": "samverschueren.linter-xo" 7 | }, 8 | "[javascript]": { 9 | "editor.formatOnPaste": true, 10 | "editor.formatOnSave": true, 11 | "editor.defaultFormatter": "samverschueren.linter-xo" 12 | }, 13 | "typescript.tsdk": "node_modules/typescript/lib", 14 | "typescript.enablePromptUseWorkspaceTsdk": true 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 enjoyjs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-mecab 2 | 3 | MeCab wrapper for Node.js 4 | 5 | ## Requirements 6 | 7 | - MeCab 8 | - [MeCab: Yet Another Part-of-Speech and Morphological Analyzer](https://taku910.github.io/mecab/) 9 | - [taku910/mecab: Yet another Japanese morphological analyzer](https://github.com/taku910/mecab) 10 | 11 | ## Install 12 | 13 | ```bash 14 | npm i @enjoyjs/node-mecab 15 | ``` 16 | 17 | ## API 18 | 19 | ### analyze(text: string, options?: [MecabOptions][mecaboptions]): Promise\ 20 | 21 | ```js 22 | import { analyze } from "@enjoyjs/node-mecab"; 23 | 24 | const result = await analyze("こんにちは世界"); 25 | console.log(result); 26 | ``` 27 | 28 | ```bash 29 | こんにちは 感動詞,*,*,*,*,*,こんにちは,コンニチハ,コンニチワ 30 | 世界 名詞,一般,*,*,*,*,世界,セカイ,セカイ 31 | EOS 32 | ``` 33 | 34 | ### analyzeSync(text: string, options?: [MecabOptions][mecaboptions]): string 35 | 36 | ```js 37 | import { analyzeSync } from "@enjoyjs/node-mecab"; 38 | 39 | const result = analyzeSync("こんにちは世界"); 40 | console.log(result); 41 | ``` 42 | 43 | --- 44 | 45 | ### tokenize(text: string, options?: [MecabOptions][mecaboptions]): Promise\<[Token][token][]\> 46 | 47 | ```js 48 | import { tokenize } from "@enjoyjs/node-mecab"; 49 | 50 | const result = await tokenize("こんにちは世界"); 51 | console.log(result); 52 | ``` 53 | 54 | ```bash 55 | [ 56 | # 省略 57 | { 58 | id: 12, 59 | surface: 'こんにちは', 60 | feature: { 61 | pos: '感動詞', 62 | posSubs: [ undefined, undefined, undefined ], 63 | conjugatedType: undefined, 64 | conjugatedForm: undefined, 65 | basicForm: 'こんにちは', 66 | reading: 'コンニチハ', 67 | pronunciation: 'コンニチワ' 68 | }, 69 | startPosition: 0, 70 | endPosition: 15, 71 | rcAttr: 3, 72 | lcAttr: 3, 73 | posid: 2, 74 | charType: 6, 75 | stat: 'NORMAL', 76 | isbest: true, 77 | alpha: 0, 78 | beta: 0, 79 | prob: 0, 80 | cost: 4033, 81 | _: [] 82 | }, 83 | { 84 | id: 30, 85 | surface: '世界', 86 | feature: { 87 | pos: '名詞', 88 | posSubs: [ '一般', undefined, undefined ], 89 | conjugatedType: undefined, 90 | conjugatedForm: undefined, 91 | basicForm: '世界', 92 | reading: 'セカイ', 93 | pronunciation: 'セカイ' 94 | }, 95 | startPosition: 15, 96 | endPosition: 21, 97 | rcAttr: 1285, 98 | lcAttr: 1285, 99 | posid: 38, 100 | charType: 2, 101 | stat: 'NORMAL', 102 | isbest: true, 103 | alpha: 0, 104 | beta: 0, 105 | prob: 0, 106 | cost: 10546, 107 | _: [] 108 | }, 109 | # 省略 110 | ] 111 | ``` 112 | 113 | ### tokenizeSync(text: string, options?: [MecabOptions][mecaboptions]): [Token][token][] 114 | 115 | ```js 116 | import { tokenizeSync } from "@enjoyjs/node-mecab"; 117 | 118 | const result = tokenizeSync("こんにちは世界"); 119 | console.log(result); 120 | ``` 121 | 122 | --- 123 | 124 | ### wakati(text: string, options?: [MecabOptions][mecaboptions]): Promise\ 125 | 126 | ```js 127 | import { wakati } from "@enjoyjs/node-mecab"; 128 | 129 | const result = await wakati("こんにちは世界"); 130 | console.log(result); 131 | ``` 132 | 133 | ```bash 134 | [ [ 'こんにちは', '世界' ] ] 135 | ``` 136 | 137 | ### wakatiSync(text: string, options?: [MecabOptions][mecaboptions]): string[][] 138 | 139 | ```js 140 | import { wakatiSync } from "@enjoyjs/node-mecab"; 141 | 142 | const result = wakatiSync("こんにちは世界"); 143 | console.log(result); 144 | ``` 145 | 146 | ## Contribution 147 | 148 | Issue、Pull requestは日本語で構いません。 149 | 不具合等ありましたらPull requestを投げていただけると幸いです。 150 | 151 | ## License 152 | 153 | [MIT License](LICENSE) 154 | 155 | [mecaboptions]: src/types.ts#L10-L36 156 | [token]: src/types.ts#L66-L114 157 | -------------------------------------------------------------------------------- /esbuild.config.ts: -------------------------------------------------------------------------------- 1 | import type {BuildOptions} from 'esbuild'; 2 | import {build} from 'esbuild'; 3 | 4 | const cjs: BuildOptions = { 5 | entryPoints: ['./src/index.ts'], 6 | bundle: true, 7 | external: ['dargs', 'execa'], 8 | outfile: './lib/index.cjs', 9 | platform: 'node', 10 | format: 'cjs', 11 | mainFields: ['module', 'main'], 12 | target: 'node16', 13 | minify: true, 14 | }; 15 | 16 | const esm: BuildOptions = { 17 | ...cjs, 18 | outfile: './lib/index.mjs', 19 | format: 'esm', 20 | }; 21 | 22 | await Promise.all([build(cjs), build(esm)]); 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@enjoyjs/node-mecab", 3 | "version": "0.5.0", 4 | "description": "MeCab wrapper for Node.js", 5 | "type": "module", 6 | "exports": { 7 | "types": "./lib/index.d.ts", 8 | "import": "./lib/index.mjs", 9 | "require": "./lib/index.cjs" 10 | }, 11 | "main": "./lib/index.cjs", 12 | "module": "./lib/index.mjs", 13 | "types": "./lib/index.d.ts", 14 | "sideEffects": false, 15 | "files": [ 16 | "lib/", 17 | "src/" 18 | ], 19 | "scripts": { 20 | "build": "run-p -cl --aggregate-output build:*", 21 | "build:main": "ts-node esbuild.config.ts", 22 | "build:types": "tsc -p tsconfig.types.json", 23 | "test": "run-p -cl --aggregate-output test:*", 24 | "test:cjs": "node test/index.cjs", 25 | "test:esm": "node test/index.mjs", 26 | "lint": "run-p -cl --aggregate-output lint:*", 27 | "lint:tsc": "tsc --noEmit", 28 | "lint:xo": "xo", 29 | "fix": "run-p -cl --aggregate-output lint:tsc fix:xo", 30 | "fix:xo": "xo --fix" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/enjoyjs/node-mecab.git" 35 | }, 36 | "keywords": [ 37 | "mecab" 38 | ], 39 | "author": "Yusei Yamamoto ", 40 | "license": "MIT", 41 | "bugs": { 42 | "url": "https://github.com/enjoyjs/node-mecab/issues" 43 | }, 44 | "homepage": "https://github.com/enjoyjs/node-mecab#readme", 45 | "engines": { 46 | "node": ">=v18.15.0" 47 | }, 48 | "packageManager": "npm@9.6.4", 49 | "devDependencies": { 50 | "@enjoyjs/tsconfig": "^5.0.0", 51 | "@swc/core": "^1.3.82", 52 | "@types/node": "^20.6.2", 53 | "@types/xo": "^0.39.6", 54 | "esbuild": "^0.19.3", 55 | "eslint-plugin-security": "^1.7.1", 56 | "eslint-plugin-sonarjs": "^0.21.0", 57 | "npm-run-all": "^4.1.5", 58 | "ts-node": "^10.9.1", 59 | "typescript": "^5.2.2", 60 | "xo": "^0.56.0" 61 | }, 62 | "dependencies": { 63 | "dargs": "^7.0.0", 64 | "execa": "^5.1.1" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/command.ts: -------------------------------------------------------------------------------- 1 | import dargs from 'dargs'; 2 | import execa from 'execa'; 3 | import {parseDump} from './parser.js'; 4 | import type {MecabOptions, Token} from './types.js'; 5 | 6 | export const analyze = async ( 7 | text: string, 8 | options: Readonly = {}, 9 | ): Promise => { 10 | const {stdout} = await execa('mecab', dargs(options), {input: text}); 11 | return stdout; 12 | }; 13 | 14 | export const analyzeSync = ( 15 | text: string, 16 | options: Readonly = {}, 17 | ): string => { 18 | const {stdout} = execa.sync('mecab', dargs(options), {input: text}); 19 | return stdout; 20 | }; 21 | 22 | export const tokenize = async ( 23 | text: string, 24 | options: Readonly = {}, 25 | ): Promise => { 26 | const dump = await analyze(text, {...options, outputFormatType: 'dump'}); 27 | return parseDump(dump); 28 | }; 29 | 30 | export const tokenizeSync = ( 31 | text: string, 32 | options: Readonly = {}, 33 | ): Token[] => { 34 | const dump = analyzeSync(text, {...options, outputFormatType: 'dump'}); 35 | return parseDump(dump); 36 | }; 37 | 38 | export const wakati = async ( 39 | text: string, 40 | options: Readonly = {}, 41 | ): Promise => { 42 | const wakati = await analyze(text, {...options, outputFormatType: 'wakati'}); 43 | return wakati.split(/\r?\n/).map((row) => row.trim().split(' ')); 44 | }; 45 | 46 | export const wakatiSync = ( 47 | text: string, 48 | options: Readonly = {}, 49 | ): string[][] => { 50 | const wakati = analyzeSync(text, {...options, outputFormatType: 'wakati'}); 51 | return wakati.split(/\r?\n/).map((row) => row.trim().split(' ')); 52 | }; 53 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './command.js'; 2 | export * from './types.js'; 3 | -------------------------------------------------------------------------------- /src/parser.ts: -------------------------------------------------------------------------------- 1 | import type {Feature, Token} from './types.js'; 2 | import {getStat, mecabNaToUndefined} from './util.js'; 3 | 4 | export const parseFeature = (feature = ''): Feature => { 5 | const [ 6 | pos, 7 | posSub1, 8 | posSub2, 9 | posSub3, 10 | conjugatedType, 11 | conjugatedForm, 12 | basicForm, 13 | reading, 14 | pronunciation, 15 | ] = feature.split(','); 16 | 17 | return { 18 | pos: mecabNaToUndefined(pos), 19 | posSubs: [ 20 | mecabNaToUndefined(posSub1), 21 | mecabNaToUndefined(posSub2), 22 | mecabNaToUndefined(posSub3), 23 | ], 24 | conjugatedType: mecabNaToUndefined(conjugatedType), 25 | conjugatedForm: mecabNaToUndefined(conjugatedForm), 26 | basicForm: mecabNaToUndefined(basicForm), 27 | reading: mecabNaToUndefined(reading), 28 | pronunciation: mecabNaToUndefined(pronunciation), 29 | }; 30 | }; 31 | 32 | export const parseDump = (dump: string): Token[] => { 33 | return dump.split(/\r?\n/).map((row) => { 34 | const values = row.split(' '); 35 | return { 36 | id: Number(values[0]), 37 | surface: values[1] ?? '', 38 | feature: parseFeature(values[2]), 39 | startPosition: Number(values[3]), 40 | endPosition: Number(values[4]), 41 | rcAttr: Number(values[5]), 42 | lcAttr: Number(values[6]), 43 | posid: Number(values[7]), 44 | charType: Number(values[8]), 45 | stat: getStat(values[9]), 46 | isbest: Boolean(Number(values[10])), 47 | alpha: Number(values[11]), 48 | beta: Number(values[12]), 49 | prob: Number(values[13]), 50 | cost: Number(values[14]), 51 | _: values.slice(15), 52 | }; 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type OutputFormatType = 2 | | 'wakati' 3 | | 'yomi' 4 | | 'chasen' 5 | | 'dump' 6 | | 'simple' 7 | | 'none' 8 | | 'em'; 9 | 10 | // Ref 1: `mecab --help` 11 | // Ref 2: http://www.mwsoft.jp/programming/munou/mecab_command.html (Japanese) 12 | export type MecabOptions = { 13 | rcfile?: string; 14 | dicdir?: string; 15 | userdic?: string; 16 | latticeLevel?: number; 17 | dictionaryInfo?: boolean; 18 | outputFormatType?: OutputFormatType; 19 | allMorphs?: boolean; 20 | nbest?: number; 21 | partial?: boolean; 22 | marginal?: boolean; 23 | maxGroupingSize?: number; 24 | nodeFormat?: string; 25 | unkFormat?: string; 26 | bosFormat?: string; 27 | eosFormat?: string; 28 | eonFormat?: string; 29 | unkFeature?: string; 30 | inputBufferSize?: number; 31 | dumpConfig?: boolean; 32 | allocateSentence?: boolean; 33 | theta?: number; 34 | costFactor?: number; 35 | output?: string; 36 | }; 37 | 38 | // Ref: https://taku910.github.io/mecab/#usage-tools (Japanese) 39 | export type Feature = { 40 | // 品詞 (part of speech) 41 | pos?: string; 42 | 43 | // 品詞細分類1, 品詞細分類2, 品詞細分類3 44 | posSubs: [string | undefined, string | undefined, string | undefined]; 45 | 46 | // 活用型 47 | conjugatedType?: string; 48 | 49 | // 活用形 50 | conjugatedForm?: string; 51 | 52 | // 原形 53 | basicForm?: string; 54 | 55 | // 読み 56 | reading?: string; 57 | 58 | // 発音 59 | pronunciation?: string; 60 | }; 61 | 62 | // Ref: https://github.com/taku910/mecab/blob/046fa78b2ed56fbd4fac312040f6d62fc1bc31e3/mecab/src/mecab.h#L218-L243 63 | export type Stats = readonly ['NORMAL', 'UNKNOWN', 'BOS', 'EOS', 'EON']; 64 | export type Stat = Stats[number]; 65 | 66 | // Ref: http://taku910.github.io/mecab/bindings.html (Japanese) 67 | export type Token = { 68 | // Node id 69 | id: number; 70 | 71 | // 形態素の文字列情報 72 | surface: string; 73 | 74 | // 素性情報 75 | feature: Feature; 76 | 77 | // 形態素の始端 78 | startPosition: number; 79 | 80 | // 形態素の終端 81 | endPosition: number; 82 | 83 | // 右文脈 id 84 | rcAttr: number; 85 | 86 | // 左文脈 id 87 | lcAttr: number; 88 | 89 | // 形態素 id 90 | posid: number; 91 | 92 | // 文字種情報 93 | charType: number; 94 | 95 | // 形態素の種類 96 | stat: Stat; 97 | 98 | // ベスト解かどうか 99 | isbest: boolean; 100 | 101 | // Forward log 確率 102 | alpha: number; 103 | 104 | // Backward log 確率 105 | beta: number; 106 | 107 | // 周辺確率 108 | prob: number; 109 | 110 | // 単語生起コスト 111 | cost: number; 112 | 113 | _: string[]; 114 | }; 115 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | import type {Stat} from './types.js'; 2 | 3 | // Convert MeCab N/A value (*) to `undefined` 4 | export const mecabNaToUndefined = (text?: string): string | undefined => { 5 | return text === '*' ? undefined : text; 6 | }; 7 | 8 | export const getStat = (statId?: string): Stat => { 9 | switch (statId) { 10 | case '0': { 11 | return 'NORMAL'; 12 | } 13 | 14 | case '1': { 15 | return 'UNKNOWN'; 16 | } 17 | 18 | case '2': { 19 | return 'BOS'; 20 | } 21 | 22 | case '3': { 23 | return 'EOS'; 24 | } 25 | 26 | case '4': { 27 | return 'EON'; 28 | } 29 | 30 | default: { 31 | return 'UNKNOWN'; 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /test/index.cjs: -------------------------------------------------------------------------------- 1 | const { 2 | analyze, 3 | analyzeSync, 4 | tokenize, 5 | tokenizeSync, 6 | wakati, 7 | wakatiSync, 8 | } = require('@enjoyjs/node-mecab'); 9 | 10 | const text = 'こんにちは世界'; 11 | 12 | Promise.all([analyze(text), tokenize(text), wakati(text)]).then((results) => { 13 | for (const result of results) { 14 | console.dir(result, {depth: null}); 15 | } 16 | }); 17 | 18 | const syncs = [analyzeSync, tokenizeSync, wakatiSync]; 19 | 20 | for (const sync of syncs) { 21 | console.dir(sync(text), {depth: null}); 22 | } 23 | -------------------------------------------------------------------------------- /test/index.mjs: -------------------------------------------------------------------------------- 1 | import { 2 | analyze, 3 | analyzeSync, 4 | tokenize, 5 | tokenizeSync, 6 | wakati, 7 | wakatiSync, 8 | } from '@enjoyjs/node-mecab'; 9 | 10 | const text = 'こんにちは世界'; 11 | 12 | Promise.all([analyze(text), tokenize(text), wakati(text)]).then((results) => { 13 | for (const result of results) { 14 | console.dir(result, {depth: null}); 15 | } 16 | }); 17 | 18 | const syncs = [analyzeSync, tokenizeSync, wakatiSync]; 19 | 20 | for (const sync of syncs) { 21 | console.dir(sync(text), {depth: null}); 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@enjoyjs/tsconfig", 3 | "include": ["src"], 4 | "compilerOptions": { 5 | "module": "Node16", 6 | "exactOptionalPropertyTypes": false 7 | }, 8 | "ts-node": { 9 | "esm": true, 10 | "swc": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.types.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "declarationMap": true, 6 | "declarationDir": "./lib", 7 | "emitDeclarationOnly": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /xo.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('xo').Options} */ 2 | module.exports = { 3 | extends: ['plugin:security/recommended', 'plugin:sonarjs/recommended'], 4 | prettier: true, 5 | rules: { 6 | 'unicorn/prefer-top-level-await': 'off', 7 | }, 8 | overrides: [ 9 | { 10 | files: '**/*.ts', 11 | rules: { 12 | '@typescript-eslint/explicit-function-return-type': 'error', 13 | }, 14 | }, 15 | ], 16 | }; 17 | --------------------------------------------------------------------------------