diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 94d7456..c9c98a8 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -1,6 +1,3 @@ -# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created -# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages - name: Node.js Package on: @@ -8,35 +5,13 @@ on: types: [created] jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - name: Install pnpm - with: - version: 10 - - uses: actions/setup-node@v4 - with: - node-version: 22 - - run: pnpm i - - run: pnpm test - publish-npm: - needs: build runs-on: ubuntu-latest permissions: id-token: write steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - name: Install pnpm - with: - version: 10 - - uses: actions/setup-node@v4 - with: - node-version: 22 - registry-url: https://registry.npmjs.org/ - - run: pnpm publish --access public --no-git-checks + - uses: actions/checkout@v6 + - uses: jdx/mise-action@v4 + - run: mise publish env: NODE_AUTH_TOKEN: ${{secrets.npm_token}} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 7c01ef2..9dd27ad 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -1,20 +1,10 @@ -# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created -# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages - name: PR on: [pull_request] jobs: - build: + tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - name: Install pnpm - with: - version: 10 - - uses: actions/setup-node@v4 - with: - node-version: 22 - - run: pnpm i - - run: pnpm test + - uses: actions/checkout@v6 + - uses: jdx/mise-action@v4 + - run: mise test diff --git a/README.md b/README.md index 053ab47..8fcb794 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,26 @@ Fast, dependency free, and responsive 7TV inline emotes parse +## Basic usage + +```sh +npm i emotesjs +``` + +```ts +import { EmotesJS } from 'emotesjs' + +let emotes = new EmotesJS({ channelId: 123456 }) + +let html = emotes.parse('this is pretty Pog') + +console.log(html) +// this is pretty Pog +``` + [Check the documentation here](https://darckfast.com/docs/emotesjs) diff --git a/index.test.js b/index.test.ts similarity index 99% rename from index.test.js rename to index.test.ts index f60a3de..c57c8bb 100644 --- a/index.test.js +++ b/index.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, test } from 'vitest' import { EmotesJS } from './index' describe('EmotesJS: parse', () => { - let emotes + let emotes: EmotesJS beforeAll(async () => { emotes = new EmotesJS({ channelId: 38746172, colon: true }) diff --git a/index.js b/index.ts similarity index 57% rename from index.js rename to index.ts index 9025a86..dbb4f16 100644 --- a/index.js +++ b/index.ts @@ -1,35 +1,144 @@ -class EmotesJS { - /** @default Map */ - #cachedEmotes = new Map(); - /** @default true */ +export interface SevenTVChannelEmotes { + id: string + platform: string + username: string + display_name: string + linked_at: number + emote_capacity: number + emote_set_id: string + emote_set: EmoteSet + user: User +} + +export interface EmoteSet { + id: string + name: string + flags: number + tags: any[] + immutable: boolean + privileged: boolean + emotes: Emote[] + emote_count: number + capacity: number + owner: any +} + +export interface Emote { + id: string + name: string + flags: number + timestamp: number + actor_id?: string + data: Data + origin_id: any +} + +export interface Data { + id: string + name: string + flags: number + lifecycle: number + state: string[] + listed: boolean + animated: boolean + owner?: Owner + host: Host + tags?: string[] +} + +export interface Owner { + id: string + username: string + display_name: string + avatar_url?: string + style: Style + role_ids: string[] + connections: Connection[] +} + +export interface Style { + color?: number + paint_id?: string + badge_id?: string +} + + + +export interface Connection { + id: string + platform: string + username: string + display_name: string + linked_at: number + emote_capacity: number + emote_set_id: string + emote_set: any +} + +export interface Host { + url: string + files: File[] +} + +export interface File { + name: string + static_name: string + width: number + height: number + frame_count: number + size: number + format: string +} + +export interface User { + id: string + username: string + display_name: string + created_at: number + avatar_url: string + style: Style + emote_sets: EmoteSet[] + editors: Editor[] + roles: string[] + connections: Connection[] +} + +export interface Editor { + id: string + permissions: number + visible: boolean + added_at: number +} + + + +interface Opts { + channelId?: number + only?: Array + usePixelDensity?: boolean + colon?: boolean + height?: string + format?: string + cache?: string + proxy?: string +} + +export class EmotesJS { + #cachedEmotes = new Map(); #colon = true; - /** @default "1.65rem" */ #height = "1.65rem"; - /** @default "WEBP" */ #format = "WEBP"; - /** @default "https://cdn.7tv.app" */ #allowedOrigins = "https://cdn.7tv.app"; - /** @default false */ #isReady = false; - /** @default false */ #usePixelDensity = false; - /** @default 0 */ total = 0; - /** @default 0 */ channelId = 0; - /** @default Promise */ isLoading = Promise.resolve(); - /** @default "" */ proxy = "" - /** - * @static - */ - static instance; - /** - * @param {Opts} opts - */ - constructor(opts) { + static instance: EmotesJS; + + constructor(opts: Opts) { if (EmotesJS.instance && EmotesJS.instance.channelId !== 0) { return EmotesJS.instance; } @@ -47,37 +156,28 @@ class EmotesJS { this.#isReady = true; } - this.proxy = opts.proxy + this.proxy = opts.proxy || "" } this.isLoading = this.load(only); EmotesJS.instance = this; } - /** - * @static - * @param {string} cache - * @returns {EmotesJS} - */ - static fromCache(cache) { + + static fromCache(cache: string) { return new EmotesJS({ cache }); } - /** - * @returns {string} - */ + cache() { return JSON.stringify(Object.fromEntries(this.#cachedEmotes.entries())); } - /** - * @param {string[]} [only=[]] - * @returns {Promise} - */ - async load(only = []) { - let globalProm = fetch("https://7tv.io/v3/emote-sets/global").then(r => { + + async load(only: Array = []) { + let globalProm: Promise = fetch("https://7tv.io/v3/emote-sets/global").then(r => { if (r.ok) { return r.json(); } throw new Error("fetch unsuccessful"); }); - let chProm = fetch("https://7tv.io/v3/users/twitch/" + this.channelId).then(r => { + let chProm: Promise = fetch("https://7tv.io/v3/users/twitch/" + this.channelId).then(r => { if (r.ok) { return r.json(); } @@ -110,7 +210,8 @@ class EmotesJS { let srcset = files.reduce((acc, curr) => { let w = `${curr.width}w`; if (this.#usePixelDensity) { - [w] = curr.name.split('.'); + //@ts-ignore + [w] = curr.name.split('.') || ''; } return `${url}/${curr.name} ${w}, ${acc}`; }, ""); @@ -120,11 +221,8 @@ class EmotesJS { this.total = this.#cachedEmotes.size; this.#isReady = true; } - /** - * @param {string | undefined} text - * @returns {string} - */ - parse(text) { + + parse(text: string) { if (!text) { console.log("no text to parse emotes"); return ""; @@ -141,11 +239,11 @@ class EmotesJS { let fullText = ""; for (let i = 0; i < words.length; i++) { let word = words[i]; - if (this.#colon && !word.startsWith(":")) { + if (this.#colon && !word?.startsWith(":")) { fullText += word + " "; continue; } - let wordKey = word.replaceAll(":", ""); + let wordKey = word?.replaceAll(":", "") || ''; let emote = this.#cachedEmotes.get(wordKey); if (emote) { fullText += emote + " "; @@ -156,14 +254,4 @@ class EmotesJS { return fullText.trim(); } } -module.exports = { EmotesJS }; -/** - * @typedef {Object} Opts - * @property {number} [channelId] - * @property {string[]} [only] - * @property {boolean} [colon] - * @property {string} [height] - * @property {"WEBP" | "AVIF"} [format] - * @property {boolean} [usePixelDensity] - * @property {string} [cache] - */ + diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..5dad366 --- /dev/null +++ b/mise.toml @@ -0,0 +1,18 @@ +[tools] +node = "26" +pnpm = "10.33" + +[tasks.prepare] +run = "pnpm install" + +[tasks.build] +depends = ["prepare"] +run = "pnpx tsup && pnpx terser ./dist/index.js -o ./dist/index.js" + +[tasks.test] +depends = ["prepare"] +run = "pnpm run test" + +[tasks.publish] +depends = ["prepare"] +run = "pnpm publish --access public --no-git-checks" diff --git a/package.json b/package.json index 2e03c63..7234e48 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "emotesjs", "description": "Fast, dependency free, and responsive 7TV inline emotes parse", - "version": "0.0.27", + "version": "1.0.0", "license": "MIT", "author": { "name": "darckfast" @@ -10,9 +10,19 @@ "node": ">=22" }, "files": [ - "index.js" + "dist" ], - "main": "index.js", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "module", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, "repository": { "type": "git", "url": "git://github.com/darckfast/emotesjs.git" @@ -25,9 +35,11 @@ ], "scripts": { "test": "vitest run", - "prepublishOnly": "pnpx terser index.js -o index.js" + "prepublishOnly": "pnpx tsup && pnpx terser ./dist/index.js -o ./dist/index.js" }, "devDependencies": { + "tsup": "^8.5.1", + "typescript": "^6.0.3", "vitest": "^3.2.4" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1bcad8f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "ignoreDeprecations": "6.0", + "module": "nodenext", + "target": "esnext", + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "strict": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true + } +} diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..0fda359 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + entry: ['index.ts'], + dts: true, + splitting: false, + sourcemap: false, + clean: true, + minify: true, + format: ["esm"], +})