mirror of
https://github.com/LBRYFoundation/Watch-on-LBRY.git
synced 2025-08-23 17:47:26 +00:00
💩 upgraded to manifest v3
This commit is contained in:
parent
7076a657fc
commit
cebdf480fe
13 changed files with 88 additions and 79 deletions
5
global.d.ts
vendored
5
global.d.ts
vendored
|
@ -2,3 +2,8 @@ declare module '*.md' {
|
||||||
var _: string
|
var _: string
|
||||||
export default _
|
export default _
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare namespace chrome
|
||||||
|
{
|
||||||
|
export const action = chrome.browserAction
|
||||||
|
}
|
|
@ -1,7 +1,17 @@
|
||||||
{
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
"name": "Watch on LBRY",
|
"name": "Watch on LBRY",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
"icons": {
|
||||||
|
"16": "assets/icons/wol/icon16.png",
|
||||||
|
"48": "assets/icons/wol/icon48.png",
|
||||||
|
"128": "assets/icons/wol/icon128.png"
|
||||||
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
|
"tabs",
|
||||||
|
"storage"
|
||||||
|
],
|
||||||
|
"host_permissions": [
|
||||||
"https://www.youtube.com/",
|
"https://www.youtube.com/",
|
||||||
"https://yewtu.be/",
|
"https://yewtu.be/",
|
||||||
"https://vid.puffyan.us/",
|
"https://vid.puffyan.us/",
|
||||||
|
@ -11,10 +21,24 @@
|
||||||
"https://lbry.tv/",
|
"https://lbry.tv/",
|
||||||
"https://odysee.com/",
|
"https://odysee.com/",
|
||||||
"https://madiator.com/",
|
"https://madiator.com/",
|
||||||
"https://finder.madiator.com/",
|
"https://finder.madiator.com/"
|
||||||
"tabs",
|
|
||||||
"storage"
|
|
||||||
],
|
],
|
||||||
|
"web_accessible_resources": [{
|
||||||
|
"resources": [
|
||||||
|
"pages/popup/index.html",
|
||||||
|
"pages/YTtoLBRY/index.html",
|
||||||
|
"pages/import/index.html",
|
||||||
|
"assets/icons/lbry/lbry-logo.svg",
|
||||||
|
"assets/icons/lbry/odysee-logo.svg",
|
||||||
|
"assets/icons/lbry/madiator-logo.svg"
|
||||||
|
],
|
||||||
|
"matches": ["<all_urls>"],
|
||||||
|
"extension_ids": []
|
||||||
|
}],
|
||||||
|
"action": {
|
||||||
|
"default_title": "Watch on LBRY",
|
||||||
|
"default_popup": "pages/popup/index.html"
|
||||||
|
},
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": [
|
"matches": [
|
||||||
|
@ -30,28 +54,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": [
|
"service_worker": "service-worker-entry-point.js"
|
||||||
"settings/background.js",
|
}
|
||||||
"scripts/background.js"
|
|
||||||
],
|
|
||||||
"persistent": true
|
|
||||||
},
|
|
||||||
"browser_action": {
|
|
||||||
"default_title": "Watch on LBRY",
|
|
||||||
"default_popup": "pages/popup/index.html"
|
|
||||||
},
|
|
||||||
"web_accessible_resources": [
|
|
||||||
"pages/popup/index.html",
|
|
||||||
"pages/YTtoLBRY/index.html",
|
|
||||||
"pages/import/index.html",
|
|
||||||
"assets/icons/lbry/lbry-logo.svg",
|
|
||||||
"assets/icons/lbry/odysee-logo.svg",
|
|
||||||
"assets/icons/lbry/madiator-logo.svg"
|
|
||||||
],
|
|
||||||
"icons": {
|
|
||||||
"16": "assets/icons/wol/icon16.png",
|
|
||||||
"48": "assets/icons/wol/icon48.png",
|
|
||||||
"128": "assets/icons/wol/icon128.png"
|
|
||||||
},
|
|
||||||
"manifest_version": 2
|
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { DialogManager } from '../../components/dialogs'
|
import type { DialogManager } from '../../components/dialogs'
|
||||||
import { getExtensionSettingsAsync, setExtensionSetting, ytUrlResolversSettings } from "../../settings"
|
import { getExtensionSettingsAsync, setExtensionSetting, ytUrlResolversSettings } from "../../settings"
|
||||||
import { getFileContent } from '../file'
|
import { getFileContent } from '../file'
|
||||||
|
|
||||||
async function generateKeys() {
|
async function generateKeys() {
|
||||||
const keys = await window.crypto.subtle.generateKey(
|
const keys = await crypto.subtle.generateKey(
|
||||||
{
|
{
|
||||||
name: "RSASSA-PKCS1-v1_5",
|
name: "RSASSA-PKCS1-v1_5",
|
||||||
// Consider using a 4096-bit key for systems that require long-term security
|
// Consider using a 4096-bit key for systems that require long-term security
|
||||||
|
@ -23,7 +23,7 @@ async function generateKeys() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function exportPrivateKey(key: CryptoKey) {
|
async function exportPrivateKey(key: CryptoKey) {
|
||||||
const exported = await window.crypto.subtle.exportKey(
|
const exported = await crypto.subtle.exportKey(
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
key
|
key
|
||||||
)
|
)
|
||||||
|
@ -34,7 +34,7 @@ const publicKeyPrefix = `MEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxA`
|
||||||
const publicKeySuffix = `IDAQAB` //`wIDAQAB` `WIDAQAB`
|
const publicKeySuffix = `IDAQAB` //`wIDAQAB` `WIDAQAB`
|
||||||
const publicKeyLength = 65
|
const publicKeyLength = 65
|
||||||
async function exportPublicKey(key: CryptoKey) {
|
async function exportPublicKey(key: CryptoKey) {
|
||||||
const exported = await window.crypto.subtle.exportKey(
|
const exported = await crypto.subtle.exportKey(
|
||||||
"spki",
|
"spki",
|
||||||
key
|
key
|
||||||
)
|
)
|
||||||
|
@ -43,7 +43,7 @@ async function exportPublicKey(key: CryptoKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function importPrivateKey(base64: string) {
|
function importPrivateKey(base64: string) {
|
||||||
return window.crypto.subtle.importKey(
|
return crypto.subtle.importKey(
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
Buffer.from(base64, 'base64'),
|
Buffer.from(base64, 'base64'),
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,7 @@ function importPrivateKey(base64: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sign(data: string, privateKey: string) {
|
export async function sign(data: string, privateKey: string) {
|
||||||
return Buffer.from(await window.crypto.subtle.sign(
|
return Buffer.from(await crypto.subtle.sign(
|
||||||
{ name: "RSASSA-PKCS1-v1_5" },
|
{ name: "RSASSA-PKCS1-v1_5" },
|
||||||
await importPrivateKey(privateKey),
|
await importPrivateKey(privateKey),
|
||||||
await crypto.subtle.digest({ name: 'SHA-1' }, Buffer.from(data))
|
await crypto.subtle.digest({ name: 'SHA-1' }, Buffer.from(data))
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
interface YtExportedJsonSubscription {
|
interface YtExportedJsonSubscription {
|
||||||
id: string
|
id: string
|
||||||
etag: string
|
etag: string
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
let db: IDBDatabase | null = null
|
let db: IDBDatabase | null = null
|
||||||
|
|
||||||
|
if (typeof chrome.extension === 'undefined') throw new Error("YT urlCache can only be accessed from extension windows and service-workers.")
|
||||||
|
|
||||||
if (typeof self.indexedDB !== 'undefined') {
|
if (typeof self.indexedDB !== 'undefined') {
|
||||||
const openRequest = indexedDB.open("yt-url-resolver-cache")
|
const openRequest = indexedDB.open("yt-url-resolver-cache")
|
||||||
openRequest.addEventListener('upgradeneeded', () => openRequest.result.createObjectStore("store").createIndex("expireAt", "expireAt"))
|
openRequest.addEventListener('upgradeneeded', () => openRequest.result.createObjectStore("store").createIndex("expireAt", "expireAt"))
|
||||||
|
@ -81,5 +83,5 @@ async function get(id: string): Promise<string | null | undefined> {
|
||||||
return response.value
|
return response.value
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LbryPathnameCache = { put, get, clearAll }
|
export const lbryUrlCache = { put, get, clearAll }
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { chunk } from "lodash"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { getExtensionSettingsAsync, ytUrlResolversSettings } from "../../settings"
|
import { getExtensionSettingsAsync, ytUrlResolversSettings } from "../../settings"
|
||||||
import { sign } from "../crypto"
|
import { sign } from "../crypto"
|
||||||
import { LbryPathnameCache } from "./urlCache"
|
import { lbryUrlCache } from "./urlCache"
|
||||||
|
|
||||||
const QUERY_CHUNK_SIZE = 100
|
const QUERY_CHUNK_SIZE = 100
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export async function resolveById(params: Paramaters, progressCallback?: (progre
|
||||||
// Check for cache first, add them to the results if there are any cache
|
// Check for cache first, add them to the results if there are any cache
|
||||||
// And remove them from the params, so we dont request for them
|
// And remove them from the params, so we dont request for them
|
||||||
params = (await Promise.all(params.map(async (item) => {
|
params = (await Promise.all(params.map(async (item) => {
|
||||||
const cachedLbryUrl = await LbryPathnameCache.get(item.id)
|
const cachedLbryUrl = await lbryUrlCache.get(item.id)
|
||||||
|
|
||||||
// Cache can be null, if there is no lbry url yet
|
// Cache can be null, if there is no lbry url yet
|
||||||
if (cachedLbryUrl !== undefined) {
|
if (cachedLbryUrl !== undefined) {
|
||||||
|
@ -58,7 +58,7 @@ export async function resolveById(params: Paramaters, progressCallback?: (progre
|
||||||
for (const item of params) {
|
for (const item of params) {
|
||||||
const lbryUrl = (item.type === 'channel' ? response.data.channels : response.data.videos)?.[item.id]?.replaceAll('#', ':') ?? null
|
const lbryUrl = (item.type === 'channel' ? response.data.channels : response.data.videos)?.[item.id]?.replaceAll('#', ':') ?? null
|
||||||
// we cache it no matter if its null or not
|
// we cache it no matter if its null or not
|
||||||
await LbryPathnameCache.put(lbryUrl, item.id)
|
await lbryUrlCache.put(lbryUrl, item.id)
|
||||||
|
|
||||||
if (lbryUrl) results[item.id] = { id: lbryUrl, type: item.type }
|
if (lbryUrl) results[item.id] = { id: lbryUrl, type: item.type }
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,21 @@ import { useState } from 'preact/hooks'
|
||||||
import { createDialogManager, Dialogs } from '../../components/dialogs'
|
import { createDialogManager, Dialogs } from '../../components/dialogs'
|
||||||
import { importProfileKeysFromFile, inputKeyFile } from '../../modules/crypto'
|
import { importProfileKeysFromFile, inputKeyFile } from '../../modules/crypto'
|
||||||
|
|
||||||
|
export async function openImportPopup() {
|
||||||
|
const importPopupWindow = open(
|
||||||
|
'/pages/import/index.html',
|
||||||
|
'Import Profile',
|
||||||
|
[
|
||||||
|
`height=${Math.max(document.body.clientHeight, screen.height * .5)}`,
|
||||||
|
`width=${document.body.clientWidth}`,
|
||||||
|
`toolbar=0,menubar=0,location=0`,
|
||||||
|
`top=${screenY}`,
|
||||||
|
`left=${screenX}`
|
||||||
|
].join(','))
|
||||||
|
close()
|
||||||
|
importPopupWindow?.focus()
|
||||||
|
}
|
||||||
|
|
||||||
function ImportPage() {
|
function ImportPage() {
|
||||||
const [loading, updateLoading] = useState(() => false)
|
const [loading, updateLoading] = useState(() => false)
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,9 @@ import { h, render } from 'preact'
|
||||||
import { useState } from 'preact/hooks'
|
import { useState } from 'preact/hooks'
|
||||||
import { createDialogManager, Dialogs } from '../../components/dialogs'
|
import { createDialogManager, Dialogs } from '../../components/dialogs'
|
||||||
import { exportProfileKeysAsFile, friendlyPublicKey, generateProfileAndSetNickname, getProfile, purgeProfile, resetProfileSettings } from '../../modules/crypto'
|
import { exportProfileKeysAsFile, friendlyPublicKey, generateProfileAndSetNickname, getProfile, purgeProfile, resetProfileSettings } from '../../modules/crypto'
|
||||||
import { LbryPathnameCache } from '../../modules/yt/urlCache'
|
import { lbryUrlCache } from '../../modules/yt/urlCache'
|
||||||
import { getTargetPlatfromSettingsEntiries, getYtUrlResolversSettingsEntiries, setExtensionSetting, useExtensionSettings } from '../../settings'
|
import { getTargetPlatfromSettingsEntiries, getYtUrlResolversSettingsEntiries, setExtensionSetting, useExtensionSettings } from '../../settings'
|
||||||
|
import { openImportPopup } from '../import/main'
|
||||||
|
|
||||||
|
|
||||||
/** Gets all the options for redirect destinations as selection options */
|
/** Gets all the options for redirect destinations as selection options */
|
||||||
|
@ -31,21 +32,6 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function importButtonClick() {
|
|
||||||
const importPopupWindow = open(
|
|
||||||
'/pages/import/index.html',
|
|
||||||
'Import Profile',
|
|
||||||
[
|
|
||||||
`height=${Math.max(document.body.clientHeight, screen.height * .5)}`,
|
|
||||||
`width=${document.body.clientWidth}`,
|
|
||||||
`toolbar=0,menubar=0,location=0`,
|
|
||||||
`top=${screenY}`,
|
|
||||||
`left=${screenX}`
|
|
||||||
].join(','))
|
|
||||||
close()
|
|
||||||
importPopupWindow?.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div id='popup'>
|
return <div id='popup'>
|
||||||
<Dialogs manager={dialogManager} />
|
<Dialogs manager={dialogManager} />
|
||||||
{
|
{
|
||||||
|
@ -100,7 +86,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
||||||
<a onClick={() => exportProfileKeysAsFile()} className={`button active`}>
|
<a onClick={() => exportProfileKeysAsFile()} className={`button active`}>
|
||||||
Export
|
Export
|
||||||
</a>
|
</a>
|
||||||
<a onClick={() => importButtonClick()}
|
<a onClick={() => openImportPopup()}
|
||||||
className={`button`}
|
className={`button`}
|
||||||
>
|
>
|
||||||
Import
|
Import
|
||||||
|
@ -139,7 +125,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
||||||
<label>You don't have a profile.</label>
|
<label>You don't have a profile.</label>
|
||||||
<p>You can either import keypair for an existing profile or generate a new profile keypair.</p>
|
<p>You can either import keypair for an existing profile or generate a new profile keypair.</p>
|
||||||
<div className='options'>
|
<div className='options'>
|
||||||
<a onClick={() => importButtonClick()} className={`button`}>
|
<a onClick={() => openImportPopup()} className={`button`}>
|
||||||
Import
|
Import
|
||||||
</a>
|
</a>
|
||||||
<a onClick={() => loads(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}>
|
<a onClick={() => loads(generateProfileAndSetNickname(dialogManager)).then(() => renderPopup())} className={`button active`}>
|
||||||
|
@ -180,7 +166,7 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<a onClick={() => loads(LbryPathnameCache.clearAll().then(() => dialogManager.alert("Cleared Cache!")))} className={`button active`}>
|
<a onClick={() => loads(lbryUrlCache.clearAll().then(() => dialogManager.alert("Cleared Cache!")))} className={`button active`}>
|
||||||
Clear Resolver Cache
|
Clear Resolver Cache
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { h, render } from 'preact'
|
import { h, render } from 'preact'
|
||||||
import { parseYouTubeURLTimeString } from '../modules/yt'
|
import { parseYouTubeURLTimeString } from '../modules/yt'
|
||||||
import { resolveById } from '../modules/yt/urlResolve'
|
import type { resolveById } from '../modules/yt/urlResolve'
|
||||||
import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, TargetPlatform, targetPlatformSettings } from '../settings'
|
import { getExtensionSettingsAsync, getSourcePlatfromSettingsFromHostname, TargetPlatform, targetPlatformSettings } from '../settings'
|
||||||
|
|
||||||
const sleep = (t: number) => new Promise(resolve => setTimeout(resolve, t))
|
const sleep = (t: number) => new Promise(resolve => setTimeout(resolve, t))
|
||||||
|
|
2
src/service-worker-entry-point.ts
Normal file
2
src/service-worker-entry-point.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import './settings/background'
|
||||||
|
import './scripts/background'
|
|
@ -1,31 +1,29 @@
|
||||||
import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync, setExtensionSetting, targetPlatformSettings, ytUrlResolversSettings } from '.'
|
import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync, setExtensionSetting, targetPlatformSettings, ytUrlResolversSettings } from '../settings'
|
||||||
|
|
||||||
/** Reset settings to default value and update the browser badge text */
|
/** Reset settings to default value and update the browser badge text */
|
||||||
async function initSettings() {
|
async function initSettings() {
|
||||||
let settings = await getExtensionSettingsAsync()
|
let settings = await getExtensionSettingsAsync()
|
||||||
|
|
||||||
// get all the values that aren't set and use them as a change set
|
// get all the values that aren't set and use them as a change set
|
||||||
const invalidEntries = (Object.entries(DEFAULT_SETTINGS) as Array<[keyof ExtensionSettings, ExtensionSettings[keyof ExtensionSettings]]>)
|
const invalidEntries = (Object.entries(DEFAULT_SETTINGS) as Array<[keyof ExtensionSettings, ExtensionSettings[keyof ExtensionSettings]]>)
|
||||||
.filter(([k]) => settings[k] === undefined || settings[k] === null)
|
.filter(([k]) => settings[k] === undefined || settings[k] === null)
|
||||||
|
|
||||||
// fix our local var and set it in storage for later
|
// fix our local var and set it in storage for later
|
||||||
if (invalidEntries.length > 0) {
|
if (invalidEntries.length > 0) {
|
||||||
const changeSet = Object.fromEntries(invalidEntries)
|
const changeSet = Object.fromEntries(invalidEntries)
|
||||||
chrome.storage.local.set(changeSet)
|
chrome.storage.local.set(changeSet)
|
||||||
settings = await getExtensionSettingsAsync()
|
settings = await getExtensionSettingsAsync()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Object.keys(targetPlatformSettings).includes(settings.targetPlatform)) setExtensionSetting('targetPlatform', DEFAULT_SETTINGS.targetPlatform)
|
if (!Object.keys(targetPlatformSettings).includes(settings.targetPlatform)) setExtensionSetting('targetPlatform', DEFAULT_SETTINGS.targetPlatform)
|
||||||
if (!Object.keys(ytUrlResolversSettings).includes(settings.urlResolver)) setExtensionSetting('urlResolver', DEFAULT_SETTINGS.urlResolver)
|
if (!Object.keys(ytUrlResolversSettings).includes(settings.urlResolver)) setExtensionSetting('urlResolver', DEFAULT_SETTINGS.urlResolver)
|
||||||
|
|
||||||
chrome.browserAction.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' })
|
chrome.action.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' })
|
||||||
}
|
}
|
||||||
|
|
||||||
chrome.storage.onChanged.addListener((changes, areaName) => {
|
chrome.storage.onChanged.addListener((changes, areaName) => {
|
||||||
if (areaName !== 'local' || !changes.redirect) return
|
if (areaName !== 'local' || !changes.redirect) return
|
||||||
chrome.browserAction.setBadgeText({ text: changes.redirect.newValue ? 'ON' : 'OFF' })
|
chrome.action.setBadgeText({ text: changes.redirect.newValue ? 'ON' : 'OFF' })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
chrome.runtime.onStartup.addListener(initSettings)
|
chrome.runtime.onStartup.addListener(initSettings)
|
||||||
chrome.runtime.onInstalled.addListener(initSettings)
|
chrome.runtime.onInstalled.addListener(initSettings)
|
|
@ -1,4 +1,4 @@
|
||||||
import { JSX } from "preact"
|
import type { JSX } from "preact"
|
||||||
import { useEffect, useReducer } from "preact/hooks"
|
import { useEffect, useReducer } from "preact/hooks"
|
||||||
|
|
||||||
export interface ExtensionSettings {
|
export interface ExtensionSettings {
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
"importsNotUsedAsValues": "error", /* Import types always with `import type` */
|
||||||
|
|
||||||
/* Module Resolution Options */
|
/* Module Resolution Options */
|
||||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
|
@ -53,7 +54,6 @@
|
||||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
|
||||||
/* Source Map Options */
|
/* Source Map Options */
|
||||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
|
Loading…
Add table
Reference in a new issue