diff --git a/.gitignore b/.gitignore index 239ecff..b23503d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules yarn.lock +distribution diff --git a/package.json b/package.json index 50190da..0f9b991 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "emoj", - "version": "4.0.0", + "version": "4.0.1", "description": "Find relevant emoji from text on the command-line", "license": "MIT", "repository": "sindresorhus/emoj", @@ -11,17 +11,18 @@ "url": "https://sindresorhus.com" }, "type": "module", - "bin": "./cli.js", + "bin": "./distribution/cli.js", "engines": { "node": ">=18" }, "scripts": { + "build": "tsc", + "prepublish": "npm run build", + "pretest": "npm run build", "test": "xo && ava" }, "files": [ - "cli.js", - "index.js", - "ui.js" + "distribution" ], "keywords": [ "cli-app", @@ -38,10 +39,10 @@ "networks" ], "dependencies": { + "@types/react": "^18.2.31", "clipboardy": "^4.0.0", "conf": "^11.0.2", "emojilib": "^3.0.11", - "import-jsx": "^5.0.0", "ink": "^4.4.1", "ink-text-input": "^5.0.1", "mem": "^9.0.2", @@ -51,10 +52,13 @@ "unicode-emoji-json": "^0.4.0" }, "devDependencies": { + "@sindresorhus/tsconfig": "^5.0.0", "ava": "^5.3.1", "eslint-config-xo-react": "^0.27.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", + "ts-node": "^10.9.1", + "typescript": "^5.2.2", "xo": "^0.56.0" }, "xo": { @@ -63,7 +67,21 @@ ], "rules": { "react/prop-types": "off", - "react/state-in-constructor": "off" + "react/state-in-constructor": "off", + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/naming-convention": "off" } + }, + "ava": { + "extensions": { + "ts": "module", + "tsx": "module" + }, + "nodeArguments": [ + "--loader=ts-node/esm" + ] } } diff --git a/cli.js b/source/cli.tsx similarity index 82% rename from cli.js rename to source/cli.tsx index 88cfc1a..a54f752 100755 --- a/cli.js +++ b/source/cli.tsx @@ -1,4 +1,4 @@ -#!/usr/bin/env NODE_NO_WARNINGS=1 node --loader=import-jsx +#!/usr/bin/env node import meow from 'meow'; import React from 'react'; import {render} from 'ink'; @@ -56,20 +56,30 @@ if (cli.flags.skinTone !== undefined) { const skinNumber = config.get('skinNumber'); const limit = Math.max(1, cli.flags.limit ?? 7); +// TODO: skin-tone package should export this. +const skinToneNames = [ + 'none', + 'white', + 'creamWhite', + 'lightBrown', + 'brown', + 'darkBrown', +] as const; + if (cli.input.length > 0) { - let emojis = await emoj(cli.input[0]); + let emojis = await emoj(cli.input[0]!); emojis = emojis .slice(0, limit) - .map(emoji => skinTone(emoji, skinNumber)); + .map(emoji => skinTone(emoji, skinToneNames[skinNumber]!)); console.log(emojis.join(' ')); if (cli.flags.copy) { - clipboardy.writeSync(emojis[0]); + clipboardy.writeSync(emojis[0]!); } } else { - let app; // eslint-disable-line prefer-const + let app: any; // eslint-disable-line prefer-const const onSelectEmoji = emoji => { clipboardy.writeSync(emoji); diff --git a/index.js b/source/index.tsx similarity index 80% rename from index.js rename to source/index.tsx index cbd6e24..4b35c0d 100644 --- a/index.js +++ b/source/index.tsx @@ -12,8 +12,9 @@ const unicodeEmojiJson = require('unicode-emoji-json'); // Substring search returns a lot of noise for shorter search words. const MIN_WORD_LENGTH_FOR_SUBSTRING_SEARCH = 4; -export default function getEmojilibEmojis(input) { - const regexSource = input.toLowerCase().split(/\s/g) +// We keep this async in case we need to to become async in the future +export default async function getEmojilibEmojis(searchQuery: string): Promise { + const regexSource = searchQuery.toLowerCase().split(/\s/g) .map(v => v.replaceAll(/\W/g, '')) .filter(v => v.length > 0) .map(v => v.length < MIN_WORD_LENGTH_FOR_SUBSTRING_SEARCH ? `^${v}$` : v) @@ -24,7 +25,7 @@ export default function getEmojilibEmojis(input) { } const regex = new RegExp(regexSource); - const emojis = []; + const emojis: string[] = []; for (const emojiCharacter of Object.keys(unicodeEmojiJson)) { const emojiData = unicodeEmojiJson[emojiCharacter]; diff --git a/ui.js b/source/ui.tsx similarity index 90% rename from ui.js rename to source/ui.tsx index 557453f..f436c7e 100644 --- a/ui.js +++ b/source/ui.tsx @@ -6,7 +6,7 @@ import mem from 'mem'; import emoj from './index.js'; // From https://usehooks.com/useDebounce/ -const useDebouncedValue = (value, delay) => { +const useDebouncedValue = (value: T, delay: number): T => { // eslint-disable-line @typescript-eslint/comma-dangle const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { @@ -60,7 +60,7 @@ const skinToneNames = [ 'lightBrown', 'brown', 'darkBrown', -]; +] as const; function Search({query, emojis, skinNumber, selectedIndex, onChangeQuery}) { const list = emojis.map((emoji, index) => ( @@ -69,7 +69,7 @@ function Search({query, emojis, skinNumber, selectedIndex, onChangeQuery}) { backgroundColor={index === selectedIndex && 'gray'} > {' '} - {skinTone(emoji, skinToneNames[skinNumber])} + {skinTone(emoji, skinToneNames[skinNumber]!)} {' '} )); @@ -95,7 +95,7 @@ function Emoj({skinNumber: initialSkinNumber, limit, onSelectEmoji}) { const [emojis, setEmojis] = useState([]); const [skinNumber, setSkinNumber] = useState(initialSkinNumber); const [selectedIndex, setSelectedIndex] = useState(0); - const [selectedEmoji, setSelectedEmoji] = useState(); + const [selectedEmoji, setSelectedEmoji] = useState(); useEffect(() => { if (selectedEmoji && stage === STAGE_COPIED) { @@ -107,7 +107,7 @@ function Emoj({skinNumber: initialSkinNumber, limit, onSelectEmoji}) { setSelectedIndex(0); setEmojis([]); setQuery(query); - }); + }, []); useEffect(() => { setStage(STAGE_SEARCH); @@ -117,7 +117,7 @@ function Emoj({skinNumber: initialSkinNumber, limit, onSelectEmoji}) { useEffect(() => { if (debouncedQuery.length <= 1) { - return; + return undefined; } let isCanceled = false; @@ -132,7 +132,7 @@ function Emoj({skinNumber: initialSkinNumber, limit, onSelectEmoji}) { } }; - run(); + run(); // eslint-disable-line @typescript-eslint/no-floating-promises return () => { isCanceled = true; @@ -147,7 +147,7 @@ function Emoj({skinNumber: initialSkinNumber, limit, onSelectEmoji}) { if (key.return) { if (emojis.length > 0) { - setSelectedEmoji(skinTone(emojis[selectedIndex], skinToneNames[skinNumber])); + setSelectedEmoji(skinTone(emojis[selectedIndex], skinToneNames[skinNumber]!)); setStage(STAGE_COPIED); } @@ -160,7 +160,7 @@ function Emoj({skinNumber: initialSkinNumber, limit, onSelectEmoji}) { const numberKey = Number(input); if (input && numberKey >= 0 && numberKey <= 9) { if (numberKey >= 1 && numberKey <= emojis.length) { - setSelectedEmoji(skinTone(emojis[numberKey - 1], skinToneNames[skinNumber])); + setSelectedEmoji(skinTone(emojis[numberKey - 1], skinToneNames[skinNumber]!)); setStage(STAGE_COPIED); } @@ -176,7 +176,7 @@ function Emoj({skinNumber: initialSkinNumber, limit, onSelectEmoji}) { } if (key.upArrow && skinNumber < 5) { - setSkinNumber(skinNumber + 1); + setSkinNumber(skinNumber + 1); // eslint-disable-line @typescript-eslint/restrict-plus-operands } if (key.downArrow && skinNumber > 0) { diff --git a/test.js b/test.tsx similarity index 89% rename from test.js rename to test.tsx index d13690b..2382e1f 100644 --- a/test.js +++ b/test.tsx @@ -1,5 +1,5 @@ import test from 'ava'; -import emoj from './index.js'; +import emoj from './source/index.js'; test('main', async t => { const [unicornEmoji] = await emoj('unicorn'); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..29e93a3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@sindresorhus/tsconfig", + "compilerOptions": { + "outDir": "distribution", + "strict": false + }, + "include": [ + "source" + ], + "ts-node": { + "transpileOnly": true, + "files": true + } +}