diff --git a/ext/bin/build.js b/ext/bin/build.js
index 6461b93..e91d045 100644
--- a/ext/bin/build.js
+++ b/ext/bin/build.js
@@ -47,24 +47,6 @@ const unpackedPath = path.join(distPath, "unpacked");
const outPath = argv.package ? unpackedPath : distPath;
-/** @type esbuild.Plugin */
-const preactCompatPlugin = {
- /**
- * Handle react/react-dom preact compat modules.
- */
- name: "preact-compat",
- setup(build) {
- const preactPath = path.resolve(
- __dirname,
- "../node_modules/preact/compat/dist/compat.module.js"
- );
-
- build.onResolve({ filter: /^(react|react-dom)$/ }, () => ({
- path: preactPath
- }));
- }
-};
-
/** @type esbuild.BuildOptions */
const buildOpts = {
bundle: true,
@@ -87,7 +69,7 @@ const buildOpts = {
// Mirroring sender
path.join(srcPath, "/cast/senders/mirroring.ts"),
// UI
- path.join(srcPath, "ui/popup/index.tsx"),
+ path.join(srcPath, "ui/popup/index.ts"),
path.join(srcPath, "ui/options/index.ts")
],
define: {
@@ -96,7 +78,6 @@ const buildOpts = {
MIRRORING_APP_ID: `"${argv.mirroringAppId}"`
},
plugins: [
- preactCompatPlugin,
// @ts-ignore
sveltePlugin({
// @ts-ignore
@@ -107,7 +88,7 @@ const buildOpts = {
copyFilesPlugin({
src: srcPath,
dest: outPath,
- excludePattern: /^(manifest\.json|.*\.(ts|tsx|js|jsx|svelte))$/
+ excludePattern: /^(manifest\.json|.*\.(ts|js|svelte))$/
})
]
};
diff --git a/ext/package-lock.json b/ext/package-lock.json
index f444e79..7cdf41e 100644
--- a/ext/package-lock.json
+++ b/ext/package-lock.json
@@ -6,12 +6,10 @@
"": {
"devDependencies": {
"@types/firefox-webext-browser": "^94.0.1",
- "@types/react": "^18.0.6",
"@types/react-dom": "^18.0.2",
"@types/semver": "^7.3.9",
"@types/uuid": "^8.3.4",
"esbuild": "^0.14.38",
- "preact": "^10.7.1",
"semver": "^7.3.7",
"ts-loader": "^9.2.8",
"typescript": "^4.6.3",
@@ -5556,16 +5554,6 @@
"node": "^10 || ^12 || >=14"
}
},
- "node_modules/preact": {
- "version": "10.7.1",
- "resolved": "https://registry.npmjs.org/preact/-/preact-10.7.1.tgz",
- "integrity": "sha512-MufnRFz39aIhs9AMFisonjzTud1PK1bY+jcJLo6m2T9Uh8AqjD77w11eAAawmjUogoGOnipECq7e/1RClIKsxg==",
- "dev": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/preact"
- }
- },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -11812,12 +11800,6 @@
"source-map-js": "^1.0.2"
}
},
- "preact": {
- "version": "10.7.1",
- "resolved": "https://registry.npmjs.org/preact/-/preact-10.7.1.tgz",
- "integrity": "sha512-MufnRFz39aIhs9AMFisonjzTud1PK1bY+jcJLo6m2T9Uh8AqjD77w11eAAawmjUogoGOnipECq7e/1RClIKsxg==",
- "dev": true
- },
"prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
diff --git a/ext/package.json b/ext/package.json
index 72b2f8d..f9b227c 100644
--- a/ext/package.json
+++ b/ext/package.json
@@ -8,12 +8,10 @@
},
"devDependencies": {
"@types/firefox-webext-browser": "^94.0.1",
- "@types/react": "^18.0.6",
"@types/react-dom": "^18.0.2",
"@types/semver": "^7.3.9",
"@types/uuid": "^8.3.4",
"esbuild": "^0.14.38",
- "preact": "^10.7.1",
"semver": "^7.3.7",
"ts-loader": "^9.2.8",
"typescript": "^4.6.3",
diff --git a/ext/src/cast/utils.ts b/ext/src/cast/utils.ts
new file mode 100644
index 0000000..c31ba01
--- /dev/null
+++ b/ext/src/cast/utils.ts
@@ -0,0 +1,29 @@
+import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types";
+import { Capability } from "./sdk/enums";
+
+/**
+ * Check receiver device capabilities bitflags against array of
+ * capability strings requested by the sender application.
+ */
+export function hasRequiredCapabilities(
+ receiverDevice: ReceiverDevice,
+ requiredCapabilities: Capability[] = []
+) {
+ const { capabilities } = receiverDevice;
+ return requiredCapabilities.every(capability => {
+ switch (capability) {
+ case Capability.AUDIO_IN:
+ return capabilities & ReceiverDeviceCapabilities.AUDIO_IN;
+ case Capability.AUDIO_OUT:
+ return capabilities & ReceiverDeviceCapabilities.AUDIO_OUT;
+ case Capability.MULTIZONE_GROUP:
+ return (
+ capabilities & ReceiverDeviceCapabilities.MULTIZONE_GROUP
+ );
+ case Capability.VIDEO_IN:
+ return capabilities & ReceiverDeviceCapabilities.VIDEO_IN;
+ case Capability.VIDEO_OUT:
+ return capabilities & ReceiverDeviceCapabilities.VIDEO_OUT;
+ }
+ });
+}
diff --git a/ext/src/ui/LoadingIndicator.svelte b/ext/src/ui/LoadingIndicator.svelte
new file mode 100644
index 0000000..c54c405
--- /dev/null
+++ b/ext/src/ui/LoadingIndicator.svelte
@@ -0,0 +1,29 @@
+
+
+{ellipsis}
diff --git a/ext/src/ui/options/Bridge.svelte b/ext/src/ui/options/Bridge.svelte
index 5344606..ba09bf9 100644
--- a/ext/src/ui/options/Bridge.svelte
+++ b/ext/src/ui/options/Bridge.svelte
@@ -2,11 +2,12 @@
import semver from "semver";
import { onMount } from "svelte";
+ import LoadingIndicator from "../LoadingIndicator.svelte";
+
import bridge, { BridgeInfo, BridgeTimedOutError } from "../../lib/bridge";
import logger from "../../lib/logger";
import { Options } from "../../lib/options";
- import { getNextEllipsis } from "../utils";
const _ = browser.i18n.getMessage;
@@ -68,8 +69,6 @@
let isCheckingUpdate = false;
let isUpdateAvailable = false;
- let checkUpdateEllipsis = "";
-
interface GitHubRelease {
url: string;
tag_name: string;
@@ -83,10 +82,6 @@
async function checkUpdate() {
isCheckingUpdate = true;
- const checkUpdateTimeout = window.setInterval(() => {
- checkUpdateEllipsis = getNextEllipsis(checkUpdateEllipsis);
- }, 500);
-
let releases: GitHubRelease[];
try {
releases = await fetch(
@@ -96,8 +91,6 @@
isCheckingUpdate = false;
updateStatus = _("optionsBridgeUpdateStatusError");
return;
- } finally {
- window.clearTimeout(checkUpdateTimeout);
}
// Ensure valid response
@@ -278,7 +271,8 @@
on:click={checkUpdate}
>
{#if isCheckingUpdate}
- {_("optionsBridgeUpdateChecking", checkUpdateEllipsis)}
+ {_("optionsBridgeUpdateChecking", "")}
{:else}
{_("optionsBridgeUpdateCheck")}
{/if}
diff --git a/ext/src/ui/popup/Popup.svelte b/ext/src/ui/popup/Popup.svelte
new file mode 100644
index 0000000..cba7a78
--- /dev/null
+++ b/ext/src/ui/popup/Popup.svelte
@@ -0,0 +1,451 @@
+
+
+
+

+ {_("popupWhitelistNotWhitelisted", knownApp?.name)}
+
+
+
+
+
+
+ {#if !receiverDevices.length}
+
+ {_("popupNoReceiversFound")}
+
+ {:else}
+ {#each receiverDevices as device}
+ {@const application = device.status?.applications?.[0]}
+ {@const isDeviceConnecting =
+ isConnecting && connectingId === device.id}
+
+ -
+
+ {device.friendlyName}
+
+
+ {application && !application.isIdleScreen
+ ? application.statusText
+ : `${device.host}:${device.port}`}
+
+
+
+ {/each}
+ {/if}
+
diff --git a/ext/src/ui/popup/index.ts b/ext/src/ui/popup/index.ts
new file mode 100644
index 0000000..f153280
--- /dev/null
+++ b/ext/src/ui/popup/index.ts
@@ -0,0 +1,16 @@
+import Popup from "./Popup.svelte";
+
+// macOS styles
+browser.runtime.getPlatformInfo().then(platformInfo => {
+ if (platformInfo.os === "mac") {
+ const link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = "styles/mac.css";
+ document.head.appendChild(link);
+ }
+});
+
+const target = document.getElementById("root");
+if (target) {
+ new Popup({ target });
+}
diff --git a/ext/src/ui/popup/index.tsx b/ext/src/ui/popup/index.tsx
deleted file mode 100755
index 8e03251..0000000
--- a/ext/src/ui/popup/index.tsx
+++ /dev/null
@@ -1,639 +0,0 @@
-/* eslint-disable max-len */
-"use strict";
-
-import React, { Component } from "react";
-import ReactDOM from "react-dom";
-
-import { menuIdPopupCast, menuIdPopupStop } from "../../background/menus";
-import type { ReceiverSelectorPageInfo } from "../../background/ReceiverSelector";
-import type { WhitelistItemData } from "../../background/whitelist";
-
-import knownApps, { KnownApp } from "../../cast/knownApps";
-import options from "../../lib/options";
-
-import messaging, { Message, Port } from "../../messaging";
-import { getNextEllipsis } from "../utils";
-import { RemoteMatchPattern } from "../../lib/matchPattern";
-
-import {
- ReceiverDevice,
- ReceiverDeviceCapabilities,
- ReceiverSelectionActionType,
- ReceiverSelectorMediaType
-} from "../../types";
-
-import { Capability } from "../../cast/sdk/enums";
-
-const _ = browser.i18n.getMessage;
-
-// macOS styles
-browser.runtime.getPlatformInfo().then(platformInfo => {
- if (platformInfo.os === "mac") {
- const link = document.createElement("link");
- link.rel = "stylesheet";
- link.href = "styles/mac.css";
- document.head.appendChild(link);
- }
-});
-
-/**
- * Check receiver device capabilities bitflags against array of
- * capability strings requested by the sender application.
- */
-function hasRequiredCapabilities(
- receiverDevice: ReceiverDevice,
- capabilities: Capability[] = []
-) {
- const { capabilities: deviceCapabilities } = receiverDevice;
- return capabilities.every(capability => {
- switch (capability) {
- case Capability.AUDIO_IN:
- return deviceCapabilities & ReceiverDeviceCapabilities.AUDIO_IN;
- case Capability.AUDIO_OUT:
- return (
- deviceCapabilities & ReceiverDeviceCapabilities.AUDIO_OUT
- );
- case Capability.MULTIZONE_GROUP:
- return (
- deviceCapabilities &
- ReceiverDeviceCapabilities.MULTIZONE_GROUP
- );
- case Capability.VIDEO_IN:
- return deviceCapabilities & ReceiverDeviceCapabilities.VIDEO_IN;
- case Capability.VIDEO_OUT:
- return (
- deviceCapabilities & ReceiverDeviceCapabilities.VIDEO_OUT
- );
- }
- });
-}
-
-interface PopupAppProps {}
-interface PopupAppState {
- /** List of devices to show in receiver list. */
- receiverDevices: ReceiverDevice[];
-
- /** Currently selected media type. */
- mediaType: ReceiverSelectorMediaType;
- /** Media types available to select. */
- availableMediaTypes: ReceiverSelectorMediaType;
-
- /** Sender app ID (if available). */
- appId?: string;
- /** Page info (if launched from page context). */
- pageInfo?: ReceiverSelectorPageInfo;
-
- /** App details (if matches known app). */
- knownApp?: KnownApp;
- /** Whether current page URL matches a whitelist pattern. */
- isPageWhitelisted: boolean;
-
- /** Whether casting to a device been initiated from this selector. */
- isConnecting: boolean;
-
- // Options
- mirroringEnabled: boolean;
- siteWhitelistEnabled: boolean;
- siteWhitelist: WhitelistItemData[];
-}
-
-class PopupApp extends Component {
- private port?: Port;
- private browserWindow?: browser.windows.Window;
-
- private resizeObserver = new ResizeObserver(() => {
- this.fitWindowHeight();
- });
-
- constructor(props: PopupAppProps) {
- super(props);
-
- this.state = {
- receiverDevices: [],
- mediaType: ReceiverSelectorMediaType.App,
- availableMediaTypes: ReceiverSelectorMediaType.App,
- isPageWhitelisted: false,
- isConnecting: false,
- mirroringEnabled: false,
- siteWhitelistEnabled: true,
- siteWhitelist: []
- };
-
- // Store window ref
- browser.windows.getCurrent().then(win => {
- this.browserWindow = win;
- });
-
- this.onMessage = this.onMessage.bind(this);
- this.onAddToWhitelist = this.onAddToWhitelist.bind(this);
- this.onReceiverCast = this.onReceiverCast.bind(this);
- this.onReceiverStop = this.onReceiverStop.bind(this);
-
- this.onContextMenu = this.onContextMenu.bind(this);
- this.onMenuShown = this.onMenuShown.bind(this);
- this.onMenuClicked = this.onMenuClicked.bind(this);
- }
-
- private onMessage(message: Message) {
- switch (message.subject) {
- case "popup:init":
- this.setState({
- appId: message.data?.appId,
- pageInfo: message.data?.pageInfo
- });
- break;
- case "popup:close":
- window.close();
- break;
-
- case "popup:update": {
- this.setState({
- /**
- * Filter receiver devices without the required
- * capabilities.
- */
- receiverDevices: message.data.receiverDevices.filter(
- receiverDevice => {
- return hasRequiredCapabilities(
- receiverDevice,
- this.state.pageInfo?.sessionRequest
- ?.capabilities
- );
- }
- )
- });
-
- const { availableMediaTypes, defaultMediaType } = message.data;
- if (
- availableMediaTypes !== undefined &&
- defaultMediaType !== undefined
- ) {
- this.setState({
- availableMediaTypes,
- mediaType: defaultMediaType
- });
- }
-
- this.updateKnownApp();
- break;
- }
- }
- }
-
- /** Resize browser window to fit content height. */
- private fitWindowHeight() {
- if (this.browserWindow?.id === undefined) {
- return;
- }
-
- browser.windows.update(this.browserWindow.id, {
- height:
- document.body.clientHeight +
- (window.outerHeight - window.innerHeight)
- });
- }
-
- private updateKnownApp() {
- const isAppMediaTypeAvailable = !!(
- this.state.availableMediaTypes & ReceiverSelectorMediaType.App
- );
-
- let knownApp: KnownApp | undefined;
-
- /**
- * Check knownApps for an app with an ID matching the registered
- * app on the target page.
- */
- if (isAppMediaTypeAvailable && this.state.appId) {
- knownApp = knownApps[this.state.appId];
- } else if (this.state.pageInfo) {
- const pageUrl = this.state.pageInfo.url;
-
- /**
- * Or if there isn't an registered app, check for an app
- * with a match pattern matching the target page URL.
- */
- for (const [, app] of Object.entries(knownApps)) {
- if (!app.matches) {
- continue;
- }
-
- const pattern = new RemoteMatchPattern(app.matches);
- if (pattern.matches(pageUrl)) {
- knownApp = app;
- break;
- }
- }
- }
-
- let isPageWhitelisted = false;
-
- // Check if target page URL is whitelisted.
- if (this.state.pageInfo) {
- for (const item of this.state.siteWhitelist) {
- const pattern = new RemoteMatchPattern(item.pattern);
- if (pattern.matches(this.state.pageInfo.url)) {
- isPageWhitelisted = true;
- break;
- }
- }
- }
-
- this.setState({ knownApp, isPageWhitelisted });
- }
-
- private async onAddToWhitelist(
- app: KnownApp,
- pageInfo: ReceiverSelectorPageInfo
- ) {
- if (!app.matches) {
- return;
- }
-
- const whitelist = await options.get("siteWhitelist");
- if (!whitelist.find(item => item.pattern === app.matches)) {
- whitelist.push({ pattern: app.matches });
- await options.set("siteWhitelist", whitelist);
-
- await browser.tabs.reload(pageInfo.tabId);
- window.close();
- }
- }
-
- private onReceiverCast(receiverDevice: ReceiverDevice) {
- this.setState({ isConnecting: true });
-
- this.port?.postMessage({
- subject: "receiverSelector:selected",
- data: {
- receiverDevice,
- actionType: ReceiverSelectionActionType.Cast,
- mediaType: this.state.mediaType
- }
- });
- }
-
- private onReceiverStop(receiverDevice: ReceiverDevice) {
- this.port?.postMessage({
- subject: "receiverSelector:stop",
- data: {
- receiverDevice,
- actionType: ReceiverSelectionActionType.Stop
- }
- });
- }
-
- private onContextMenu(ev: MouseEvent) {
- if (!(ev.target instanceof Element)) return;
-
- const receiverElement = ev.target.closest(".receiver");
- if (receiverElement) {
- browser.menus.overrideContext({
- showDefaults: false
- });
- }
- }
-
- private getDeviceFromElement(target: Element) {
- const receiverElement = target.closest(".receiver");
- if (!receiverElement) return;
-
- const receiverElementIndex = [
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- ...receiverElement.parentElement!.children
- ].indexOf(receiverElement);
-
- // Match by index rendered receiver element to device array
- if (receiverElementIndex > -1) {
- return this.state.receiverDevices[receiverElementIndex];
- }
- }
-
- /** Handle show events for receiver context menus. */
- private onMenuShown(info: browser.menus._OnShownInfo) {
- if (!info.targetElementId) return;
- const target = browser.menus.getTargetElement(info.targetElementId);
- if (!target) return;
-
- const device = this.getDeviceFromElement(target);
- if (!device) {
- browser.menus.update(menuIdPopupCast, { visible: false });
- browser.menus.update(menuIdPopupStop, { visible: false });
- } else {
- const app = device.status?.applications?.[0];
- const isAppRunning = !!(app && !app.isIdleScreen);
-
- browser.menus.update(menuIdPopupCast, {
- visible: true,
- title: _("popupCastMenuTitle", device.friendlyName),
- enabled:
- // Not already connecting to a receiver
- !this.state.isConnecting &&
- // Selected media type available
- !!(this.state.availableMediaTypes & this.state.mediaType)
- });
-
- browser.menus.update(menuIdPopupStop, {
- visible: isAppRunning,
- title: isAppRunning
- ? _("popupStopMenuTitle", [
- app.displayName,
- device.friendlyName
- ])
- : ""
- });
- }
-
- browser.menus.refresh();
- }
-
- /** Handle click events for receiver context menus. */
- private onMenuClicked(info: browser.menus.OnClickData) {
- if (
- info.menuItemId !== menuIdPopupCast &&
- info.menuItemId !== menuIdPopupStop
- ) {
- return;
- }
-
- if (!info.targetElementId) return;
- const target = browser.menus.getTargetElement(info.targetElementId);
- if (!target) return;
-
- const device = this.getDeviceFromElement(target);
- if (!device) return;
-
- switch (info.menuItemId) {
- case menuIdPopupCast:
- this.onReceiverCast(device);
- break;
- case menuIdPopupStop:
- this.onReceiverStop(device);
- break;
- }
- }
-
- public async componentDidMount() {
- this.port = messaging.connect({ name: "popup" });
- this.port.onMessage.addListener(this.onMessage);
-
- // Start observing content size changes
- this.resizeObserver.observe(document.body);
-
- options.getAll().then(opts => {
- this.setState({
- mirroringEnabled: opts.mirroringEnabled,
- siteWhitelistEnabled: opts.siteWhitelistEnabled,
- siteWhitelist: opts.siteWhitelist
- });
- });
-
- this.updateKnownApp();
-
- window.addEventListener("contextmenu", this.onContextMenu);
- browser.menus.onClicked.addListener(this.onMenuClicked);
- browser.menus.onShown.addListener(this.onMenuShown);
- }
-
- public componentWillUnmount() {
- this.port?.disconnect();
- this.resizeObserver.disconnect();
-
- window.removeEventListener("contextmenu", this.onContextMenu);
- browser.menus.onClicked.removeListener(this.onMenuClicked);
- browser.menus.onShown.removeListener(this.onMenuShown);
- }
-
- public componentDidUpdate() {
- setTimeout(() => {
- this.fitWindowHeight();
- }, 1);
- }
-
- public render() {
- const isAppMediaTypeSelected =
- this.state.mediaType === ReceiverSelectorMediaType.App;
- const isTabMediaTypeSelected =
- this.state.mediaType === ReceiverSelectorMediaType.Tab;
- const isScreenMediaTypeSelected =
- this.state.mediaType === ReceiverSelectorMediaType.Screen;
-
- const isAppMediaTypeAvailable = !!(
- this.state.availableMediaTypes & ReceiverSelectorMediaType.App
- );
-
- return (
- <>
-
-

- {_(
- "popupWhitelistNotWhitelisted",
- this.state.knownApp?.name
- )}
-
-
-
-
-
- {_("popupMediaSelectCastLabel")}
-
-
-
-
-
- {_("popupMediaSelectToLabel")}
-
-
-
-
- >
- );
- }
-}
-
-interface ReceiverProps {
- details: ReceiverDevice;
- isMediaTypeAvailable: boolean;
- isAnyConnecting: boolean;
-
- // Events
- onCast(receiverDevice: ReceiverDevice): void;
- onStop(receiverDevice: ReceiverDevice): void;
-}
-interface ReceiverState {
- isConnecting: boolean;
- connectingEllipsis: string;
-}
-class Receiver extends Component {
- private ellipsisInterval?: number;
-
- constructor(props: ReceiverProps) {
- super(props);
-
- this.state = {
- isConnecting: false,
- connectingEllipsis: ""
- };
-
- this.handleCast = this.handleCast.bind(this);
- }
-
- private handleCast() {
- if (!this.props.details.status) {
- return;
- }
-
- this.ellipsisInterval = window.setInterval(() => {
- this.setState(state => ({
- connectingEllipsis: getNextEllipsis(state.connectingEllipsis)
- }));
- }, 500);
-
- this.setState({ isConnecting: true });
- this.props.onCast(this.props.details);
- }
-
- componentWillUnmount() {
- window.clearInterval(this.ellipsisInterval);
- }
-
- render() {
- const application = this.props.details.status?.applications?.[0];
-
- return (
-
-
- {this.props.details.friendlyName}
-
-
- {application && !application.isIdleScreen
- ? application.statusText
- : `${this.props.details.host}:${this.props.details.port}`}
-
-
-
- );
- }
-}
-
-// Render after CSS has loaded
-window.addEventListener("load", () => {
- ReactDOM.render(, document.querySelector("#root"));
-});
diff --git a/ext/src/ui/utils.tsx b/ext/src/ui/utils.tsx
deleted file mode 100644
index 153ed13..0000000
--- a/ext/src/ui/utils.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-"use strict";
-
-import React, { useEffect, useState } from "react";
-
-export function getNextEllipsis(ellipsis: string): string {
- if (ellipsis === "") return ".";
- if (ellipsis === ".") return "..";
- if (ellipsis === "..") return "...";
- if (ellipsis === "...") return "";
-
- return "";
-}
-
-export interface LoadingIndicatorProps {
- text: string;
- duration?: number;
-}
-export function LoadingIndicator(props: LoadingIndicatorProps) {
- const [ellipsis, setEllipsis] = useState("");
-
- useEffect(() => {
- const interval = window.setInterval(() => {
- setEllipsis(prev => getNextEllipsis(prev));
- }, props.duration ?? 500);
-
- return () => {
- window.clearInterval(interval);
- };
- }, []);
-
- return (
-
- {props.text}
- {ellipsis}
-
- );
-}
diff --git a/ext/tsconfig.json b/ext/tsconfig.json
index da4ad81..220c4d5 100644
--- a/ext/tsconfig.json
+++ b/ext/tsconfig.json
@@ -1,7 +1,6 @@
{
"extends": "../tsconfig",
"compilerOptions": {
- "jsx": "react",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"moduleResolution": "node",
"sourceMap": true,