diff --git a/package.json b/package.json index ad1c8489..e768d7c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@editorjs/embed", - "version": "2.7.3", + "version": "2.7.4", "keywords": [ "codex editor", "embed", @@ -25,7 +25,7 @@ "scripts": { "dev": "vite", "build": "vite build", - "test": "mocha -r @babel/register -r ignore-styles --recursive ./test", + "test": "mocha --require ts-node/register --require ignore-styles --recursive './test/**/*.ts'", "lint": "eslint src/ --ext .js", "lint:errors": "eslint src/ --ext .js --quiet", "lint:fix": "eslint src/ --ext .js --fix" @@ -39,7 +39,9 @@ "@babel/plugin-transform-runtime": "^7.23.2", "@babel/preset-env": "^7.23.2", "@babel/register": "^7.22.15", + "@types/chai": "^4.3.16", "@types/debounce": "^1.2.4", + "@types/mocha": "^10.0.6", "@types/node": "^20.14.2", "chai": "^4.2.0", "debounce": "^1.2.0", diff --git a/src/index.ts b/src/index.ts index 4ea68c5d..ae5405ca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,13 @@ import SERVICES from './services'; import './index.css'; import { debounce } from 'debounce'; +import { ServiceConfig, ServicesConfigType } from './types/types'; import { API , PatternPasteEventDetail } from '@editorjs/editorjs'; /** * @description Embed Tool data */ -interface EmbedData { +export interface EmbedData { /** Service name */ service: string; /** Source URL of embedded content */ @@ -21,30 +22,12 @@ interface EmbedData { caption?: string; } -/** - * @description Service configuration object - */ -interface Service { - /** Pattern of source URLs */ - regex: RegExp; - /** URL scheme to embedded page. Use '<%= remote_id %>' to define a place to insert resource id */ - embedUrl: string; - /** Iframe which contains embedded content */ - html: string; - /** Function to get resource id from RegExp groups */ - id?: (ids: string[]) => string; - /** Embedded content width */ - width?: number; - /** Embedded content height */ - height?: number; -} - /** * @description Embed tool configuration object */ interface EmbedConfig { /** Additional services provided by user */ - services?: { [key: string]: Service | boolean }; + services?: ServicesConfigType; } /** @@ -99,7 +82,7 @@ export default class Embed { /** Read-only mode flag */ private readOnly: boolean; /** Static property with available services */ - static services: { [key: string]: Service }; + static services: { [key: string]: ServiceConfig }; /** Static property with patterns for paste handling configuration */ static patterns: { [key: string]: RegExp }; /** @@ -302,9 +285,9 @@ export default class Embed { .filter(([key, value]) => { return typeof value === 'object'; }) - .filter(([key, service]) => Embed.checkServiceConfig(service as Service)) + .filter(([key, service]) => Embed.checkServiceConfig(service as ServiceConfig)) .map(([key, service]) => { - const { regex, embedUrl, html, height, width, id } = service as Service; + const { regex, embedUrl, html, height, width, id } = service as ServiceConfig; return [key, { regex, @@ -313,7 +296,7 @@ export default class Embed { height, width, id, - } ] as [string, Service]; + } ] as [string, ServiceConfig]; }); if (enabledServices.length) { @@ -322,9 +305,9 @@ export default class Embed { entries = entries.concat(userServices); - Embed.services = entries.reduce<{ [key: string]: Service }>((result, [key, service]) => { + Embed.services = entries.reduce<{ [key: string]: ServiceConfig }>((result, [key, service]) => { if (!(key in result)) { - result[key] = service as Service; + result[key] = service as ServiceConfig; return result; } @@ -336,7 +319,9 @@ export default class Embed { Embed.patterns = entries .reduce<{ [key: string]: RegExp }>((result, [key, item]) => { - result[key] = item.regex as RegExp; + if (item && typeof item !== 'boolean') { + result[key] = (item as ServiceConfig).regex as RegExp; + } return result; }, {}); @@ -348,7 +333,7 @@ export default class Embed { * @param {Service} config - configuration of embed block element * @returns {boolean} */ - static checkServiceConfig(config: Service): boolean { + static checkServiceConfig(config: ServiceConfig): boolean { const { regex, embedUrl, html, height, width, id } = config; let isValid = Boolean(regex && regex instanceof RegExp) && diff --git a/src/services.ts b/src/services.ts index a0cd1dce..e8188cd9 100644 --- a/src/services.ts +++ b/src/services.ts @@ -1,14 +1,7 @@ /* eslint-disable no-useless-escape */ -interface Service { - regex: RegExp; - embedUrl: string; - html: string; - id?: (ids: string[]) => string; - width?: number; - height?: number; -} +import { ServiceConfig, ServicesConfigType } from './types/types'; -const SERVICES: { [key: string]: Service } = { +const SERVICES: ServicesConfigType = { vimeo: { regex: /(?:http[s]?:\/\/)?(?:www.)?(?:player.)?vimeo\.co(?:.+\/([^\/]\d+)(?:#t=[\d]+)?s?$)/, embedUrl: 'https://player.vimeo.com/video/<%= remote_id %>?title=0&byline=0', diff --git a/src/types/types.ts b/src/types/types.ts new file mode 100644 index 00000000..56e85fcc --- /dev/null +++ b/src/types/types.ts @@ -0,0 +1,23 @@ + +/** + * @description Service configuration object + */ +export interface ServiceConfig { + /** Pattern of source URLs */ + regex: RegExp; + /** URL scheme to embedded page. Use '<%= remote_id %>' to define a place to insert resource id */ + embedUrl: string; + /** Iframe which contains embedded content */ + html: string; + /** Function to get resource id from RegExp groups */ + id?: (ids: string[]) => string; + /** Embedded content width */ + width?: number; + /** Embedded content height */ + height?: number; +} + +/** + * @description Type for services configuration + */ +export type ServicesConfigType = { [key: string]: ServiceConfig | boolean }; \ No newline at end of file diff --git a/test/services.js b/test/services.ts similarity index 97% rename from test/services.js rename to test/services.ts index 2447dc02..b756d868 100644 --- a/test/services.js +++ b/test/services.ts @@ -1,12 +1,13 @@ import { expect } from 'chai'; -import EmbedTool from '../src/index'; +import EmbedTool, { EmbedData } from '../src/index'; +import { API } from '@editorjs/editorjs'; -EmbedTool.prepare({}); +EmbedTool.prepare({config: {}}); const { patterns } = EmbedTool.pasteConfig; -const embed = new EmbedTool({ data: {} }); +const embed = new EmbedTool({data: {} as EmbedData, api: {} as API, readOnly: false}); -const composePasteEventMock = (type, service, url) => ({ +const composePasteEventMock = (type: string, service: string, url: string) => ({ type, detail: { key: service, @@ -348,7 +349,7 @@ describe('Services Regexps', () => { return pattern.test(url); }); - expect(foundService[0]).to.be.equal(name); + expect(foundService![0]).to.be.equal(name); }); }); diff --git a/yarn.lock b/yarn.lock index 95643a12..6373bef0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1374,6 +1374,11 @@ resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== +"@types/chai@^4.3.16": + version "4.3.16" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.16.tgz#b1572967f0b8b60bf3f87fe1d854a5604ea70c82" + integrity sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ== + "@types/debounce@^1.2.4": version "1.2.4" resolved "https://registry.yarnpkg.com/@types/debounce/-/debounce-1.2.4.tgz#cb7e85d9ad5ababfac2f27183e8ac8b576b2abb3" @@ -1394,6 +1399,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/mocha@^10.0.6": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.6.tgz#818551d39113081048bdddbef96701b4e8bb9d1b" + integrity sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg== + "@types/node@^20.14.2": version "20.14.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18"