An open API service indexing awesome lists of open source software.

https://github.com/hexdrinker/remark-cjk-adjacent-strong


https://github.com/hexdrinker/remark-cjk-adjacent-strong

Last synced: 1 day ago
JSON representation

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.