Skip to content

Commit

Permalink
Merge branch 'ts'
Browse files Browse the repository at this point in the history
  • Loading branch information
rhysd committed May 16, 2024
2 parents 91da030 + adddf66 commit 023b810
Show file tree
Hide file tree
Showing 15 changed files with 1,187 additions and 1,837 deletions.
1 change: 0 additions & 1 deletion .eslintignore

This file was deleted.

49 changes: 0 additions & 49 deletions .eslintrc.json

This file was deleted.

10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: ['16', '18', '20']
node: ['18', '20', '22']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
Expand All @@ -22,10 +22,10 @@ jobs:
name: Linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: npm
- run: npm ci
- run: npm run lint
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
/node_modules
/*.js
/*.js.map
/*.d.ts

23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ remark-emoji
[![npm][npm-badge]][npm]

[remark-emoji][npm] is a [remark](https://github.com/remarkjs/remark) plugin to replace `:emoji:` to real UTF-8
emojis in text. Accessibility support and Emoticon support are optionally available.
emojis in Markdown text. Accessibility support and Emoticon support are optionally available.

## Demo

Expand All @@ -17,15 +17,15 @@ remark().use(emoji [, options]);
```

```javascript
import {remark} from 'remark';
import { remark } from 'remark';
import emoji from 'remark-emoji';

const doc = 'Emojis in this text will be replaced: :dog: :+1:';
const doc = 'Emojis in this text will be replaced: :dog::+1:';
const processor = remark().use(emoji);
const file = await processor.process(doc);

console.log(String(file));
// => Emojis in this text will be replaced: 🐶 👍
// => Emojis in this text will be replaced: 🐶👍
```

Note that this package is [ESM only][esm-only] from v3.0.0 since remark packages migrated to ESM.
Expand All @@ -36,15 +36,17 @@ Note that this package is [ESM only][esm-only] from v3.0.0 since remark packages

Setting to `true` makes the converted emoji text accessible with `role` and `aria-label` attributes. Each emoji
text is wrapped with `<span>` element. The `role` and `aria-label` attribute are not allowed by default. Please
add them to the sanitization schema used by remark's HTML transformer.
add them to the sanitization schema used by remark's HTML transformer. The default sanitization schema is defined
in [hast-util-sanitize](https://www.npmjs.com/package/hast-util-sanitize) package.

For example,

```javascript
import {remark} from 'remark';
import remarkParse from 'remark-parse';
import toHtml from 'remark-html';
import {defaultSchema} from 'hast-util-sanitize'
import { defaultSchema } from 'hast-util-sanitize'
import emoji from 'remark-emoji';
import { unified } from 'unified'

// Allow using `role` and `aria-label` attributes in transformed HTML document
const schema = structuredClone(defaultSchema);
Expand All @@ -54,7 +56,8 @@ if ('span' in schema.attributes) {
schema.attributes.span = ['role', 'ariaLabel'];
}

const processor = remark()
const processor = unified()
.use(remarkParse)
.use(emoji, { accessible: true })
.use(toHtml, { sanitize: schema });
const file = await processor.process('Hello :dog:!');
Expand Down Expand Up @@ -95,8 +98,8 @@ Distributed under [the MIT License](LICENSE).



[ci-badge]: https://github.com/rhysd/remark-emoji/workflows/CI/badge.svg?branch=master&event=push
[ci]: https://github.com/rhysd/remark-emoji/actions?query=workflow%3ACI
[ci-badge]: https://github.com/rhysd/remark-emoji/actions/workflows/ci.yml/badge.svg
[ci]: https://github.com/rhysd/remark-emoji/actions/workflows/ci.yml
[npm-badge]: https://badge.fury.io/js/remark-emoji.svg
[npm]: https://www.npmjs.com/package/remark-emoji
[esm-only]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
60 changes: 60 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// @ts-check

import eslint from '@eslint/js';
import ts from 'typescript-eslint';
import mocha from 'eslint-plugin-mocha';
import configPrettier from 'eslint-config-prettier';
import n from 'eslint-plugin-n';
import security from 'eslint-plugin-security';

export default ts.config(
eslint.configs.recommended,
...ts.configs.recommendedTypeChecked,
n.configs['flat/recommended-module'],
security.configs.recommended,
configPrettier,
{
ignores: ['*.d.ts'],
},
{
files: ['*.ts'],
languageOptions: {
parserOptions: {
project: 'tsconfig.json',
},
},
},
{
files: ['*.ts', 'eslint.config.mjs'],
rules: {
indent: ['error', 4],
quotes: ['error', 'single'],
'linebreak-style': ['error', 'unix'],
semi: ['error', 'always'],
eqeqeq: ['error', 'always'],
'no-constant-condition': ['error', { checkLoops: false }],
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'n/no-missing-import': 'off', // This rule causes false positives on type-only packages
},
},
{
files: ['eslint.config.mjs'],
languageOptions: {
parserOptions: {
project: 'tsconfig.eslint.json',
},
},
},
mocha.configs.flat.recommended,
{
files: ['test.ts'],
rules: {
'mocha/no-exclusive-tests': 'error',
'mocha/no-pending-tests': 'error',
'mocha/no-skipped-tests': 'error',
'mocha/no-top-level-hooks': 'error',
},
}
);
50 changes: 40 additions & 10 deletions index.js → index.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,57 @@
import { get as getEmoji } from 'node-emoji';
import { emoticon } from 'emoticon';
import { findAndReplace } from 'mdast-util-find-and-replace';
import { findAndReplace, type Find, type Replace } from 'mdast-util-find-and-replace';
import type { Plugin } from 'unified';
import type { Root, Nodes, Text } from 'mdast';

const RE_EMOJI = /:\+1:|:-1:|:[\w-]+:/g;
const RE_SHORT = /[$@|*'",;.=:\-)([\]\\/<>038BOopPsSdDxXzZ]{2,5}/g;
const RE_PUNCT = /(?:_|-(?!1))/g;

const DEFAULT_SETTINGS = {
/**
* Configuration of remark-emoji plugin.
*/
export interface RemarkEmojiOptions {
/**
* Makes converted emoji and emoticon texts accessible by wrapping them with
* `span` element setting `role` and `aria-label` attributes.
*
* @defaultValue false
*/
accessible?: boolean;
/**
* Adds an extra whitespace after emoji.
* Useful when browser handle emojis with half character length and
* the following character is hidden.
*
* @defaultValue false
*/
padSpaceAfter?: boolean;
/**
* Whether to support emoticon shortcodes (e.g. :-) will be replaced by 😃)
*
* @defaultValue false
*/
emoticon?: boolean;
}

const DEFAULT_SETTINGS: RemarkEmojiOptions = {
padSpaceAfter: false,
emoticon: false,
accessible: false,
};

export default function plugin(options) {
const plugin: Plugin<[(RemarkEmojiOptions | null | undefined)?], Root> = options => {
const settings = Object.assign({}, DEFAULT_SETTINGS, options);
const pad = !!settings.padSpaceAfter;
const emoticonEnable = !!settings.emoticon;
const accessible = !!settings.accessible;

function aria(text, label) {
function aria(text: string, label: string): Text {
// Creating HTML node in Markdown node is undocumented.
// https://github.com/syntax-tree/mdast-util-math/blob/e70bb824dc70f5423324b31b0b68581cf6698fe8/index.js#L44-L55
return {
type: 'text',
meta: null,
value: text,
data: {
hName: 'span',
Expand All @@ -36,7 +64,7 @@ export default function plugin(options) {
};
}

function replaceEmoticon(match) {
function replaceEmoticon(match: string): string | false | Text {
// find emoji by shortcode - full match or with-out last char as it could be from text e.g. :-),
const iconFull = emoticon.find(e => e.emoticons.includes(match)); // full match
const iconPart = emoticon.find(e => e.emoticons.includes(match.slice(0, -1))); // second search pattern
Expand All @@ -53,7 +81,7 @@ export default function plugin(options) {
return replaced;
}

function replaceEmoji(match) {
function replaceEmoji(match: string): string | false | Text {
let got = getEmoji(match);

if (typeof got === 'undefined') {
Expand All @@ -72,14 +100,16 @@ export default function plugin(options) {
return got;
}

const replacers = [[RE_EMOJI, replaceEmoji]];
const replacers: [Find, Replace][] = [[RE_EMOJI, replaceEmoji]];
if (emoticonEnable) {
replacers.push([RE_SHORT, replaceEmoticon]);
}

function transformer(tree) {
function transformer(tree: Nodes): void {
findAndReplace(tree, replacers);
}

return transformer;
}
};

export default plugin;
Loading

0 comments on commit 023b810

Please sign in to comment.