Skip to content

Commit

Permalink
feat: 조사 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
rhseung committed Oct 18, 2024
1 parent aaf425d commit 7558a82
Show file tree
Hide file tree
Showing 16 changed files with 424 additions and 76 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,4 @@ dist
.yarn/install-state.gz
.pnp.*

.idea
5 changes: 0 additions & 5 deletions .idea/.gitignore

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/jsLibraryMappings.xml

This file was deleted.

8 changes: 0 additions & 8 deletions .idea/modules.xml

This file was deleted.

14 changes: 0 additions & 14 deletions .idea/ts-module-template.iml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/vcs.xml

This file was deleted.

28 changes: 5 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,7 @@
# 🧩 typescript-module
# 🔤 Hangul

TypeScript를 사용한 모듈을 쉽게 만들기 위한 템플릿 레포지토리.
```js
require('hangul');

- Bot App: [`메신저봇R`](https://github.com/MessengerBotTeam/msgbot-old-release/releases/tag/0.7.36a) (0.7.36a 버전 이상)
- CI/CD: [`GitHub Actions`](https://github.com/features/actions)
- Module Bundler: [`Rollup`](https://rollupjs.org/)
- Test: [`Vitest`](https://vitest.dev/)
- Execute: [`TSX`](https://tsx.is/)
- Runtime: [`Node.js`](https://nodejs.org/en)

## 📜 features
- `npm run build:all-in-one`: `src` 폴더의 모든 소스를 하나의 `.js`, `.d.ts` 파일로 만들어 `dist/all`에 빌드
- `npm run build:no-all-in-one`: `src` 폴더의 각 파일들을 파일 구조를 유지한 채 `.js`, `.d.ts` 파일로 변환해 `dist/preserve`에 빌드
- `package.json``version` 키가 변경된 채로 푸쉬하면 자동으로 해당 버전으로 릴리즈 생성. (`v1.0.0-alpha` 처럼 쓰면 prerelease로 설정됨)

> [!NOTE]
> `package.json` 에서 `directories` 키를 수정하여 빌드 타겟 변경 가능
## ✅ prepare
1. 레포지토리 설정에서 `GitHub Actions` 가 적절한 권한을 가지고 있는지 확인하세요.
- `Settings > Actions > General` 로 이동합니다.
- `Workflow permissions` 에서 `Read and write permissions` 를 선택하고 저장합니다.
2. `package.json` 파일을 열어 `name`, `version`, `description`, `author`, `license` 등의 필드를 적절히 수정합니다.
3. `npm install` 명령어를 실행하여 의존성 패키지를 설치합니다.
console.log(''.이가 + ' 꼬꼬댁 울고, 오리'.가 + ' 꽥꽥 운다.');
```
6 changes: 5 additions & 1 deletion examples/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
// example code
import '../src';
import { disassemble } from 'hangul';

"안녕하세요".isHangul();
disassemble("안녕하세요");
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ts-module-template",
"version": "1.0.0",
"description": "template for typescript module",
"name": "hangul",
"version": "0.1.0-beta",
"description": "hangul helper module",
"main": "src/index.ts",
"directories": {
"example": "./examples",
Expand Down
22 changes: 22 additions & 0 deletions src/hangul.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export function _isHangul(text: string): boolean {
return text.length > 0 && [...text].every(ch => '가' <= ch && ch <= '힣');
}

export function isHangul(text: string): boolean {
return /^[-]+$/.test(text);
}

// export function disassemble(text: string): string[];
// export function disassembleGroups(text: string): string[][];
// export function assemble(letters: string[]): string;

// export function to예사소리(letter: string): string;
// export function to거센소리(letter: string): string;
// export function to된소리(letter: string): string;

// export function canBe초성(letter: string): boolean;
// export function canBe중성(letter: string): boolean;
// export function canBe종성(letter: string): boolean;

// export function has받침();
// ...
9 changes: 1 addition & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1 @@
const bot = BotManager.getCurrentBot();

bot.addListener(Event.MESSAGE, msg => {
if (msg.content === '/ping')
msg.reply('pong!');
else if (msg.content === '/hello')
msg.reply(`Hello, ${msg.author.name}!`);
});
export * from './josa';
233 changes: 233 additions & 0 deletions src/josa.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import { describe, test, expect } from 'vitest';

import { get조사, apply조사 } from './josa';
import './josa';

describe('josa', () => {
test('주격조사', () => {
expect(apply조사('샴푸', '이/가')).toBe('샴푸가');
expect('샴푸'.이가).toBe('샴푸가');
expect('샴푸'.).toBe('샴푸가');
expect('샴푸'.).toBe('샴푸가');
expect(apply조사('칫솔', '이/가')).toBe('칫솔이');
expect('칫솔'.이가).toBe('칫솔이');
expect('칫솔'.).toBe('칫솔이');
expect('칫솔'.).toBe('칫솔이');
});
test('목적격조사', () => {
expect(apply조사('샴푸', '을/를')).toBe('샴푸를');
expect('샴푸'.을를).toBe('샴푸를');
expect('샴푸'.).toBe('샴푸를');
expect('샴푸'.).toBe('샴푸를');
expect(apply조사('칫솔', '을/를')).toBe('칫솔을');
expect('칫솔'.을를).toBe('칫솔을');
expect('칫솔'.).toBe('칫솔을');
expect('칫솔'.).toBe('칫솔을');
});
test('대조의 보조사', () => {
expect(apply조사('샴푸', '은/는')).toBe('샴푸는');
expect('샴푸'.은는).toBe('샴푸는');
expect('샴푸'.).toBe('샴푸는');
expect('샴푸'.).toBe('샴푸는');
expect(apply조사('칫솔', '은/는')).toBe('칫솔은');
expect('칫솔'.은는).toBe('칫솔은');
expect('칫솔'.).toBe('칫솔은');
expect('칫솔'.).toBe('칫솔은');
});
test('방향의 격조사', () => {
expect(apply조사('바깥', '으로/로')).toBe('바깥으로');
expect('바깥'.으로).toBe('바깥으로');
expect('바깥'.).toBe('바깥으로');
expect(apply조사('내부', '으로/로')).toBe('내부로');
expect('내부'.으로).toBe('내부로');
expect('내부'.).toBe('내부로');
});
test('방향의 격조사 ㄹ 받침 예외 처리', () => {
expect(apply조사('지름길', '으로/로')).toBe('지름길로');
expect('지름길'.으로).toBe('지름길로');
expect('지름길'.).toBe('지름길로');
});
test('비교의 격조사', () => {
expect(apply조사('샴푸', '와/과')).toBe('샴푸와');
expect('샴푸'.와과).toBe('샴푸와');
expect('샴푸'.).toBe('샴푸와');
expect('샴푸'.).toBe('샴푸와');
expect(apply조사('칫솔', '와/과')).toBe('칫솔과');
expect('칫솔'.와과).toBe('칫솔과');
expect('칫솔'.).toBe('칫솔과');
expect('칫솔'.).toBe('칫솔과');
});
test('선택의 보조사', () => {
expect(apply조사('샴푸', '이나/나')).toBe('샴푸나');
expect('샴푸'.이나).toBe('샴푸나');
expect('샴푸'.).toBe('샴푸나');
expect(apply조사('칫솔', '이나/나')).toBe('칫솔이나');
expect('칫솔'.이나).toBe('칫솔이나');
expect('칫솔'.).toBe('칫솔이나');
});
test('화제의 보조사', () => {
expect(apply조사('샴푸', '이란/란')).toBe('샴푸란');
expect('샴푸'.이란).toBe('샴푸란');
expect('샴푸'.).toBe('샴푸란');
expect(apply조사('칫솔', '이란/란')).toBe('칫솔이란');
expect('칫솔'.이란).toBe('칫솔이란');
expect('칫솔'.).toBe('칫솔이란');
});
test('호격조사', () => {
expect(apply조사('철수', '아/야')).toBe('철수야');
expect('철수'.아야).toBe('철수야');
expect('철수'.).toBe('철수야');
expect('철수'.).toBe('철수야');
expect(apply조사('길동', '아/야')).toBe('길동아');
expect('길동'.아야).toBe('길동아');
expect('길동'.).toBe('길동아');
expect('길동'.).toBe('길동아');
});
test('접속조사', () => {
expect(apply조사('고기', '이랑/랑')).toBe('고기랑');
expect('고기'.이랑).toBe('고기랑');
expect('고기'.).toBe('고기랑');
expect(apply조사('과일', '이랑/랑')).toBe('과일이랑');
expect('과일'.이랑).toBe('과일이랑');
expect('과일'.).toBe('과일이랑');
});
test('주제의 보조사', () => {
expect(apply조사('의사', '이라/라')).toBe('의사라');
expect('의사'.이라).toBe('의사라');
expect('의사'.).toBe('의사라');
expect(apply조사('선생님', '이라/라')).toBe('선생님이라');
expect('선생님'.이라).toBe('선생님이라');
expect('선생님'.).toBe('선생님이라');
});
test('서술격조사와 종결어미', () => {
expect(apply조사('사과', '이에요/예요')).toBe('사과예요');
expect('사과'.이에요).toBe('사과예요');
expect('사과'.예요).toBe('사과예요');
expect(apply조사('책', '이에요/예요')).toBe('책이에요');
expect('책'.이에요).toBe('책이에요');
expect('책'.예요).toBe('책이에요');
});
test('서술격조사와 종결어미, "이" 로 끝나는 단어 예외 처리', () => {
expect(apply조사('때밀이', '이에요/예요')).toBe('때밀이예요');
expect('때밀이'.이에요).toBe('때밀이예요');
expect('때밀이'.예요).toBe('때밀이예요');
});
test('지위나 신분 또는 자격을 나타내는 위격조사', () => {
expect(apply조사('학생', '으로서/로서')).toBe('학생으로서');
expect('학생'.으로서).toBe('학생으로서');
expect('학생'.로서).toBe('학생으로서');
expect(apply조사('부모', '으로서/로서')).toBe('부모로서');
expect('부모'.으로서).toBe('부모로서');
expect('부모'.로서).toBe('부모로서');
});
test('지위나 신분 또는 자격을 나타내는 위격조사 ㄹ 받침 예외 처리', () => {
expect(apply조사('라이벌', '으로서/로서')).toBe('라이벌로서');
expect('라이벌'.으로서).toBe('라이벌로서');
expect('라이벌'.로서).toBe('라이벌로서');
});
test('수단의 의미를 나타내는 도구격조사', () => {
expect(apply조사('토큰', '으로써/로써')).toBe('토큰으로써');
expect('토큰'.으로써).toBe('토큰으로써');
expect('토큰'.로써).toBe('토큰으로써');
expect(apply조사('함수', '으로써/로써')).toBe('함수로써');
expect('함수'.으로써).toBe('함수로써');
expect('함수'.로써).toBe('함수로써');
});
test('수단의 의미를 나타내는 도구격조사 ㄹ 받침 예외 처리', () => {
expect(apply조사('건물', '으로써/로써')).toBe('건물로써');
expect('건물'.으로써).toBe('건물로써');
expect('건물'.로써).toBe('건물로써');
});
test('어떤 행동의 출발점이나 비롯되는 대상임을 나타내는 격 조사', () => {
expect(apply조사('역삼동', '으로부터/로부터')).toBe('역삼동으로부터');
expect('역삼동'.으로부터).toBe('역삼동으로부터');
expect('역삼동'.로부터).toBe('역삼동으로부터');
expect(apply조사('저기', '으로부터/로부터')).toBe('저기로부터');
expect('저기'.으로부터).toBe('저기로부터');
expect('저기'.로부터).toBe('저기로부터');
});
test('어떤 행동의 출발점이나 비롯되는 대상임을 나타내는 격 조사 ㄹ 받침 예외 처리', () => {
expect(apply조사('동굴', '으로부터/로부터')).toBe('동굴로부터');
expect('동굴'.으로부터).toBe('동굴로부터');
expect('동굴'.로부터).toBe('동굴로부터');
});
test('단어가 빈 문자열일 경우 빈 문자열을 반환한다.', () => {
expect(apply조사('', '이/가')).toBe('');
expect(''.이가).toBe('');
expect(''.).toBe('');
expect(''.).toBe('');
});
});

describe('josa.pick', () => {
test('첫 번째 매개변수가 빈 문자열이라면 옵션 중 첫 번째 값을 반환한다.', () => {
expect(get조사('', '이/가')).toBe('이');
});
test('주격조사', () => {
expect(get조사('샴푸', '이/가')).toBe('가');
expect(get조사('칫솔', '이/가')).toBe('이');
});
test('목적격조사', () => {
expect(get조사('샴푸', '을/를')).toBe('를');
expect(get조사('칫솔', '을/를')).toBe('을');
});
test('대조의 보조사', () => {
expect(get조사('샴푸', '은/는')).toBe('는');
expect(get조사('칫솔', '은/는')).toBe('은');
});
test('방향의 격조사', () => {
expect(get조사('바깥', '으로/로')).toBe('으로');
expect(get조사('내부', '으로/로')).toBe('로');
});
test('방향의 격조사 ㄹ 받침 예외 처리', () => {
expect(get조사('지름길', '으로/로')).toBe('로');
});
test('비교의 격조사', () => {
expect(get조사('샴푸', '와/과')).toBe('와');
expect(get조사('칫솔', '와/과')).toBe('과');
});
test('선택의 보조사', () => {
expect(get조사('샴푸', '이나/나')).toBe('나');
expect(get조사('칫솔', '이나/나')).toBe('이나');
});
test('화제의 보조사', () => {
expect(get조사('샴푸', '이란/란')).toBe('란');
expect(get조사('칫솔', '이란/란')).toBe('이란');
});
test('호격조사', () => {
expect(get조사('철수', '아/야')).toBe('야');
expect(get조사('길동', '아/야')).toBe('아');
});
test('접속조사', () => {
expect(get조사('고기', '이랑/랑')).toBe('랑');
expect(get조사('과일', '이랑/랑')).toBe('이랑');
});
test('서술격조사와 종결어미', () => {
expect(get조사('사과', '이에요/예요')).toBe('예요');
expect(get조사('책', '이에요/예요')).toBe('이에요');
});
test('서술격조사와 종결어미, "이" 로 끝나는 단어 예외 처리', () => {
expect(get조사('때밀이', '이에요/예요')).toBe('예요');
});
test('지위나 신분 또는 자격을 나타내는 위격조사', () => {
expect(get조사('학생', '으로서/로서')).toBe('으로서');
expect(get조사('부모', '으로서/로서')).toBe('로서');
});
test('지위나 신분 또는 자격을 나타내는 위격조사 ㄹ 받침 예외 처리', () => {
expect(get조사('라이벌', '으로서/로서')).toBe('로서');
});
test('수단의 의미를 나타내는 도구격조사', () => {
expect(get조사('토큰', '으로써/로써')).toBe('으로써');
expect(get조사('함수', '으로써/로써')).toBe('로써');
});
test('수단의 의미를 나타내는 도구격조사 ㄹ 받침 예외 처리', () => {
expect(get조사('건물', '으로써/로써')).toBe('로써');
});
test('어떤 행동의 출발점이나 비롯되는 대상임을 나타내는 격 조사', () => {
expect(get조사('역삼동', '으로부터/로부터')).toBe('으로부터');
expect(get조사('저기', '으로부터/로부터')).toBe('로부터');
});
test('어떤 행동의 출발점이나 비롯되는 대상임을 나타내는 격 조사 ㄹ 받침 예외 처리', () => {
expect(get조사('동굴', '으로부터/로부터')).toBe('로부터');
});
});
Loading

0 comments on commit 7558a82

Please sign in to comment.