https://github.com/hexdrinker/remark-cjk-adjacent-strong
https://github.com/hexdrinker/remark-cjk-adjacent-strong
Last synced: 1 day ago
JSON representation
- Host: GitHub
- URL: https://github.com/hexdrinker/remark-cjk-adjacent-strong
- Owner: hexdrinker
- License: mit
- Created: 2026-03-21T11:11:21.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-03-21T16:05:26.000Z (4 months ago)
- Last Synced: 2026-03-22T02:24:29.685Z (4 months ago)
- Language: JavaScript
- Homepage: https://remark-cjk-adjacent-strong.vercel.app
- Size: 72.3 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.en.md
- License: LICENSE
Awesome Lists containing this project
README
# remark-cjk-adjacent-strong
`remark-cjk-adjacent-strong` is a `remark` plugin that repairs a narrow Markdown/MDX parsing gap where `**...**` emphasis remains plain text because it is immediately followed by CJK text.
Typical examples:
- `**커피(coffee)**가`
- `**コーヒー(coffee)**が`
- `**咖啡(coffee)**是`
## README languages
- [한국어](./README.md)
- [English](./README.en.md)
- [日本語](./README.ja.md)
- [简体中文](./README.zh-CN.md)
## When should you use this plugin?
Use it when the closing `**` is immediately followed by CJK text, so the default parser leaves the sequence as literal text instead of building emphasis.
This usually happens when the last character inside `**...**` is punctuation or a symbol such as `)`, `?`, `!`, quotes, or brackets.
## Package direction
- Public package name: `remark-cjk-adjacent-strong`
- Default entry targets broad CJK adjacency
- `./hangul` exposes a Hangul-only preset
- `./core` exposes the shared factory
## Install
```bash
npm install remark-cjk-adjacent-strong
```
## Compatibility
- Node.js 18+
- ESM package
- Works in `remarkPlugins` chains used by `next-mdx-remote`, `@next/mdx`, Contentlayer, Astro MDX, and plain `unified` pipelines
## Which entry point should I use?
### Default package entry
```ts
import remarkCjkAdjacentStrong from 'remark-cjk-adjacent-strong'
```
Use this when you want to repair adjacency for:
- Hangul
- Han
- Hiragana
- Katakana
### Hangul-only entry
```ts
import remarkHangulAdjacentStrong from 'remark-cjk-adjacent-strong/hangul'
```
Use this when you only want Korean adjacency repair.
### Core factory
```ts
import { createRemarkAdjacentStrong } from 'remark-cjk-adjacent-strong/core'
const remarkAsciiAdjacentStrong = createRemarkAdjacentStrong(/[A-Z]/u)
```
Use this when you want the same rewrite engine with your own suffix-character rule.
## Integration examples
### Plain unified pipeline
```ts
import remarkCjkAdjacentStrong from 'remark-cjk-adjacent-strong'
import remarkParse from 'remark-parse'
import { unified } from 'unified'
const processor = unified()
.use(remarkParse)
.use(remarkCjkAdjacentStrong)
```
### `remarkPlugins` chain
```ts
import remarkCjkAdjacentStrong from 'remark-cjk-adjacent-strong'
import remarkGfm from 'remark-gfm'
const mdxOptions = {
remarkPlugins: [remarkGfm, remarkCjkAdjacentStrong],
}
```
## Exact rewrite rule
The transformer only rewrites plain `text` nodes when all conditions below are true.
1. The text contains a `**X**Y` pattern.
2. `X` does not contain `*` or a newline.
3. The last character of `X` matches `/[\p{P}\p{S}]/u`.
4. The first character of `Y` matches the configured suffix-character pattern.
5. The text is not inside a protected node.
Default CJK suffix pattern:
```ts
/[\p{Script=Hangul}\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u
```
Hangul-only suffix pattern:
```ts
/[\p{Script=Hangul}\p{Script=Hangul_Jamo}\p{Script=Hangul_Compatibility_Jamo}]/u
```
## Covered examples
### Repaired by the default CJK entry
| Source | Result |
| --- | --- |
| `**커피(coffee)**가 필요하다.` | `커피(coffee)가 필요하다.` |
| `**브런치(brunch)**를 먹으러 가자.` | `브런치(brunch)를 먹으러 가자.` |
| `**コーヒー(coffee)**が好きだ。` | `コーヒー(coffee)が好きだ。` |
| `**拿铁(latte)**也很受欢迎。` | `拿铁(latte)也很受欢迎。` |
| `**咖啡(coffee)**是必需品。` | `咖啡(coffee)是必需品。` |
### Repaired by the Hangul-only entry
| Source | Result |
| --- | --- |
| `**커피(coffee)**가 필요하다.` | `커피(coffee)가 필요하다.` |
| `**디저트(dessert)**를 곁들이자.` | `디저트(dessert)를 곁들이자.` |
| `**라테(latte)**를 마셔 보자.` | `라테(latte)를 마셔 보자.` |
| `**커피(coffee)!**가 먼저다.` | `커피(coffee)!가 먼저다.` |
| `**모카(mocha).**도 괜찮다.` | `모카(mocha).도 괜찮다.` |
## Intentionally not covered
### Cases already handled by CommonMark
- `**커피**는 이미 잘 파싱된다.`
- `**커피(coffee)** 가 필요하다.`
- `**커피(coffee)**!`
- `설명 예시는 **coffee**이렇게도 붙을 수 있다.`
### Non-target suffixes
- `**咖啡(coffee)**abc`
- `**咖啡(coffee)**123`
### Protected nodes
- `code`
- `inlineCode`
- `html`
- `strong`
- `emphasis`
- `delete`
- `link`
- `mdx*`
Examples that stay literal:
- `` `**コーヒー(coffee)**が` ``
- `[**咖啡(coffee)**是](https://example.com)`
- `**커피(coffee)**가`
- `~~**라테(latte)**를~~`
## Known tradeoff
This plugin is conservative, but it can still convert documentation-style literal examples into real `strong` nodes.
Example:
- `설명 예시는 **커피(coffee)**가 라고 적는다고 착각할 수 있다.`
If a literal example must stay literal, put it inside a protected context such as inline code.
Representative side-effect candidates are listed in `fixtures/manual-check.mdx`.
## Package structure
- `src/core.ts`: shared traversal and rewrite engine
- `src/hangul.ts`: Hangul-only preset
- `src/index.ts`: default CJK preset and main entry
## API
### Default export
- `remarkCjkAdjacentStrong`
### Named exports
- `remarkCjkAdjacentStrong`
- `CJK_SUFFIX_CHARACTER`
- `remarkHangulAdjacentStrong`
- `HANGUL_SUFFIX_CHARACTER`
- `createRemarkAdjacentStrong`
## Development
```bash
npm install
npm run verify
npm run pack:check
```
## Publish checklist
1. Run `npm run verify`
2. Run `npm run pack:check`
3. Confirm `package.json` metadata
4. Run `npm publish`
`prepublishOnly` is configured, so publish re-runs verification and a dry-run pack check.