From aa45fc89b08446aabbe2df16b0d765b47865b2bf Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 26 Jan 2023 15:26:33 -0700 Subject: [PATCH 1/3] Add baseUrl option to loadPlugins in linear-genome-view --- packages/core/PluginLoader.ts | 10 +++++----- .../src/loadPlugins.ts | 7 +++++-- .../src/loadPlugins.ts | 7 +++++-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/core/PluginLoader.ts b/packages/core/PluginLoader.ts index 4f377c50ec..932236d476 100644 --- a/packages/core/PluginLoader.ts +++ b/packages/core/PluginLoader.ts @@ -133,7 +133,7 @@ export default class PluginLoader { ) } - async loadCJSPlugin(def: CJSPluginDefinition, windowHref: string) { + async loadCJSPlugin(def: CJSPluginDefinition, windowHref?: string) { const parsedUrl = new URL(def.cjsUrl, windowHref) if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') { throw new Error( @@ -147,7 +147,7 @@ export default class PluginLoader { return this.fetchCJS(parsedUrl.href) } - async loadESMPlugin(def: ESMPluginDefinition, windowHref: string) { + async loadESMPlugin(def: ESMPluginDefinition, windowHref?: string) { const parsedUrl = 'esmUrl' in def ? new URL(def.esmUrl, windowHref) @@ -170,7 +170,7 @@ export default class PluginLoader { async loadUMDPlugin( def: UMDPluginDefinition | LegacyUMDPluginDefinition, - windowHref: string, + windowHref?: string, ) { const parsedUrl = 'url' in def @@ -200,7 +200,7 @@ export default class PluginLoader { return plugin } - async loadPlugin(def: PluginDefinition, windowHref: string) { + async loadPlugin(def: PluginDefinition, windowHref?: string) { let plugin: LoadedPlugin if (isElectron && isCJSPluginDefinition(def)) { plugin = await this.loadCJSPlugin(def, windowHref) @@ -229,7 +229,7 @@ export default class PluginLoader { ) } - async load(windowHref = '') { + async load(windowHref?: string) { return Promise.all( this.definitions.map(async definition => ({ plugin: await this.loadPlugin(definition, windowHref), diff --git a/products/jbrowse-react-circular-genome-view/src/loadPlugins.ts b/products/jbrowse-react-circular-genome-view/src/loadPlugins.ts index 790cd73958..23faffafb4 100644 --- a/products/jbrowse-react-circular-genome-view/src/loadPlugins.ts +++ b/products/jbrowse-react-circular-genome-view/src/loadPlugins.ts @@ -7,9 +7,12 @@ interface PluginDefinition { export default async function loadPlugins( pluginDefinitions: PluginDefinition[], - args?: { fetchESM: (url: string) => Promise }, + args?: { + fetchESM?: (url: string) => Promise + baseUrl?: string + }, ) { const pluginLoader = new PluginLoader(pluginDefinitions, args) pluginLoader.installGlobalReExports(window) - return pluginLoader.load('') + return pluginLoader.load(args?.baseUrl) } diff --git a/products/jbrowse-react-linear-genome-view/src/loadPlugins.ts b/products/jbrowse-react-linear-genome-view/src/loadPlugins.ts index 790cd73958..23faffafb4 100644 --- a/products/jbrowse-react-linear-genome-view/src/loadPlugins.ts +++ b/products/jbrowse-react-linear-genome-view/src/loadPlugins.ts @@ -7,9 +7,12 @@ interface PluginDefinition { export default async function loadPlugins( pluginDefinitions: PluginDefinition[], - args?: { fetchESM: (url: string) => Promise }, + args?: { + fetchESM?: (url: string) => Promise + baseUrl?: string + }, ) { const pluginLoader = new PluginLoader(pluginDefinitions, args) pluginLoader.installGlobalReExports(window) - return pluginLoader.load('') + return pluginLoader.load(args?.baseUrl) } From 25bfee86bc25e85a1b54faf3cf6ff27f75f2e04d Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 26 Jan 2023 15:32:10 -0700 Subject: [PATCH 2/3] Add error handling for loadPlugins --- packages/core/PluginLoader.ts | 36 +-- .../JBrowseLinearGenomeView.stories.tsx | 207 +++++++++--------- 2 files changed, 125 insertions(+), 118 deletions(-) diff --git a/packages/core/PluginLoader.ts b/packages/core/PluginLoader.ts index 932236d476..94534214f1 100644 --- a/packages/core/PluginLoader.ts +++ b/packages/core/PluginLoader.ts @@ -5,9 +5,13 @@ import ReExports from './ReExports' import { isElectron } from './util' export interface UMDLocPluginDefinition { - umdLoc: { uri: string; baseUri?: string } + umdLoc: { + uri: string + baseUri?: string + } name: string } + export interface UMDUrlPluginDefinition { umdUrl: string name: string @@ -78,10 +82,6 @@ export interface LoadedPlugin { default: PluginConstructor } -export function getWindowPath(windowHref: string) { - return window.location.href + windowHref -} - function getGlobalObject(): Window { // Based on window-or-global // https://github.com/purposeindustries/window-or-global/blob/322abc71de0010c9e5d9d0729df40959e1ef8775/lib/index.js @@ -133,8 +133,8 @@ export default class PluginLoader { ) } - async loadCJSPlugin(def: CJSPluginDefinition, windowHref?: string) { - const parsedUrl = new URL(def.cjsUrl, windowHref) + async loadCJSPlugin(def: CJSPluginDefinition, baseUri?: string) { + const parsedUrl = new URL(def.cjsUrl, baseUri) if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') { throw new Error( `Cannot load plugins using protocol "${parsedUrl.protocol}"`, @@ -147,10 +147,10 @@ export default class PluginLoader { return this.fetchCJS(parsedUrl.href) } - async loadESMPlugin(def: ESMPluginDefinition, windowHref?: string) { + async loadESMPlugin(def: ESMPluginDefinition, baseUri?: string) { const parsedUrl = 'esmUrl' in def - ? new URL(def.esmUrl, windowHref) + ? new URL(def.esmUrl, baseUri) : new URL(def.esmLoc.uri, def.esmLoc.baseUri) if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') { @@ -170,13 +170,13 @@ export default class PluginLoader { async loadUMDPlugin( def: UMDPluginDefinition | LegacyUMDPluginDefinition, - windowHref?: string, + baseUri?: string, ) { const parsedUrl = 'url' in def - ? new URL(def.url, windowHref) + ? new URL(def.url, baseUri) : 'umdUrl' in def - ? new URL(def.umdUrl, windowHref) + ? new URL(def.umdUrl, baseUri) : new URL(def.umdLoc.uri, def.umdLoc.baseUri) if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') { @@ -200,14 +200,14 @@ export default class PluginLoader { return plugin } - async loadPlugin(def: PluginDefinition, windowHref?: string) { + async loadPlugin(def: PluginDefinition, baseUri?: string) { let plugin: LoadedPlugin if (isElectron && isCJSPluginDefinition(def)) { - plugin = await this.loadCJSPlugin(def, windowHref) + plugin = await this.loadCJSPlugin(def, baseUri) } else if (isESMPluginDefinition(def)) { - plugin = await this.loadESMPlugin(def, windowHref) + plugin = await this.loadESMPlugin(def, baseUri) } else if (isUMDPluginDefinition(def)) { - plugin = await this.loadUMDPlugin(def, windowHref) + plugin = await this.loadUMDPlugin(def, baseUri) } else if (!isElectron && isCJSPluginDefinition(def)) { throw new Error( `CommonJS plugin found, but not in a NodeJS environment: ${JSON.stringify( @@ -229,10 +229,10 @@ export default class PluginLoader { ) } - async load(windowHref?: string) { + async load(baseUri?: string) { return Promise.all( this.definitions.map(async definition => ({ - plugin: await this.loadPlugin(definition, windowHref), + plugin: await this.loadPlugin(definition, baseUri), definition, })), ) diff --git a/products/jbrowse-react-linear-genome-view/stories/JBrowseLinearGenomeView.stories.tsx b/products/jbrowse-react-linear-genome-view/stories/JBrowseLinearGenomeView.stories.tsx index 090b6cb37a..5f1998ad4b 100644 --- a/products/jbrowse-react-linear-genome-view/stories/JBrowseLinearGenomeView.stories.tsx +++ b/products/jbrowse-react-linear-genome-view/stories/JBrowseLinearGenomeView.stories.tsx @@ -1,7 +1,6 @@ /* eslint-disable no-console */ import React, { useEffect, useState } from 'react' import { observer } from 'mobx-react' -import { PluginRecord } from '@jbrowse/core/PluginLoader' import { Region } from '@jbrowse/core/util/types' import ViewType from '@jbrowse/core/pluggableElementTypes/ViewType' import PluginManager from '@jbrowse/core/PluginManager' @@ -596,6 +595,9 @@ export const Hg38Exome = () => { return } export const WithExternalPlugins = () => { + const [error, setError] = useState() + const [viewState, setViewState] = + useState>() // usage with buildtime plugins // this plugins array is then passed to the createViewState constructor // @@ -606,112 +608,117 @@ export const WithExternalPlugins = () => { // import {loadPlugins} from '@jbrowse/react-linear-genome-view' // // we manually call loadPlugins, and pass the result to the createViewState constructor - const [plugins, setPlugins] = useState() useEffect(() => { // eslint-disable-next-line @typescript-eslint/no-floating-promises ;(async () => { - const loadedPlugins = await loadPlugins([ - { - name: 'UCSC', - url: 'https://unpkg.com/jbrowse-plugin-ucsc@^1/dist/jbrowse-plugin-ucsc.umd.production.min.js', - }, - ]) - setPlugins(loadedPlugins) - })() - }, [setPlugins]) - - if (!plugins) { - return null - } - - const state = createViewState({ - assembly: { - name: 'hg19', - aliases: ['GRCh37'], - sequence: { - type: 'ReferenceSequenceTrack', - trackId: 'Pd8Wh30ei9R', - adapter: { - type: 'BgzipFastaAdapter', - fastaLocation: { - uri: 'https://jbrowse.org/genomes/hg19/fasta/hg19.fa.gz', - locationType: 'UriLocation', - }, - faiLocation: { - uri: 'https://jbrowse.org/genomes/hg19/fasta/hg19.fa.gz.fai', - locationType: 'UriLocation', - }, - gziLocation: { - uri: 'https://jbrowse.org/genomes/hg19/fasta/hg19.fa.gz.gzi', - locationType: 'UriLocation', - }, - }, - }, - refNameAliases: { - adapter: { - type: 'RefNameAliasAdapter', - location: { - uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/hg19/hg19_aliases.txt', - locationType: 'UriLocation', - }, - }, - }, - }, - plugins: plugins.map(p => p.plugin), - tracks: [ - { - type: 'FeatureTrack', - trackId: 'segdups_ucsc_hg19', - name: 'UCSC SegDups', - category: ['Annotation'], - assemblyNames: ['hg19'], - adapter: { - type: 'UCSCAdapter', - track: 'genomicSuperDups', - }, - }, - ], - location: '1:2,467,681..2,667,681', - defaultSession: { - name: 'Runtime plugins', - view: { - id: 'aU9Nqje1U', - type: 'LinearGenomeView', - offsetPx: 22654, - bpPerPx: 108.93300653594771, - displayedRegions: [ + try { + const plugins = await loadPlugins([ { - refName: '1', - start: 0, - end: 249250621, - reversed: false, - assemblyName: 'hg19', + name: 'UCSC', + url: 'https://unpkg.com/jbrowse-plugin-ucsc@^1/dist/jbrowse-plugin-ucsc.umd.production.min.js', }, - ], - tracks: [ - { - id: 'MbiRphmDa', - type: 'FeatureTrack', - configuration: 'segdups_ucsc_hg19', - displays: [ - { - id: '8ovhuA5cFM', - type: 'LinearBasicDisplay', - height: 100, - configuration: 'segdups_ucsc_hg19-LinearBasicDisplay', + ]) + const state = createViewState({ + assembly: { + name: 'hg19', + aliases: ['GRCh37'], + sequence: { + type: 'ReferenceSequenceTrack', + trackId: 'Pd8Wh30ei9R', + adapter: { + type: 'BgzipFastaAdapter', + fastaLocation: { + uri: 'https://jbrowse.org/genomes/hg19/fasta/hg19.fa.gz', + locationType: 'UriLocation', + }, + faiLocation: { + uri: 'https://jbrowse.org/genomes/hg19/fasta/hg19.fa.gz.fai', + locationType: 'UriLocation', + }, + gziLocation: { + uri: 'https://jbrowse.org/genomes/hg19/fasta/hg19.fa.gz.gzi', + locationType: 'UriLocation', + }, }, - ], + }, + refNameAliases: { + adapter: { + type: 'RefNameAliasAdapter', + location: { + uri: 'https://s3.amazonaws.com/jbrowse.org/genomes/hg19/hg19_aliases.txt', + locationType: 'UriLocation', + }, + }, + }, }, - ], - hideHeader: false, - hideHeaderOverview: false, - trackSelectorType: 'hierarchical', - trackLabels: 'overlapping', - showCenterLine: false, - }, - }, - }) - return + plugins: plugins.map(p => p.plugin) || [], + tracks: [ + { + type: 'FeatureTrack', + trackId: 'segdups_ucsc_hg19', + name: 'UCSC SegDups', + category: ['Annotation'], + assemblyNames: ['hg19'], + adapter: { + type: 'UCSCAdapter', + track: 'genomicSuperDups', + }, + }, + ], + location: '1:2,467,681..2,667,681', + defaultSession: { + name: 'Runtime plugins', + view: { + id: 'aU9Nqje1U', + type: 'LinearGenomeView', + offsetPx: 22654, + bpPerPx: 108.93300653594771, + displayedRegions: [ + { + refName: '1', + start: 0, + end: 249250621, + reversed: false, + assemblyName: 'hg19', + }, + ], + tracks: [ + { + id: 'MbiRphmDa', + type: 'FeatureTrack', + configuration: 'segdups_ucsc_hg19', + displays: [ + { + id: '8ovhuA5cFM', + type: 'LinearBasicDisplay', + height: 100, + configuration: 'segdups_ucsc_hg19-LinearBasicDisplay', + }, + ], + }, + ], + hideHeader: false, + hideHeaderOverview: false, + trackSelectorType: 'hierarchical', + trackLabels: 'overlapping', + showCenterLine: false, + }, + }, + }) + setViewState(state) + } catch (e) { + setError(e) + } + })() + }, []) + + return error ? ( +
{`${error}`}
+ ) : !viewState ? ( +
Loading...
+ ) : ( + + ) } export const WithInternetAccounts = () => { From 7a85bf225d74fb3909d1e64f58446055494cdae1 Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 26 Jan 2023 23:48:41 -0700 Subject: [PATCH 3/3] Small typescripting --- packages/core/PluginLoader.ts | 12 +++++++----- .../src/loadPlugins.ts | 4 ++-- .../src/loadPlugins.ts | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/core/PluginLoader.ts b/packages/core/PluginLoader.ts index 94534214f1..91386147ca 100644 --- a/packages/core/PluginLoader.ts +++ b/packages/core/PluginLoader.ts @@ -101,13 +101,13 @@ function isInWebWorker(globalObject: ReturnType) { export default class PluginLoader { definitions: PluginDefinition[] = [] - fetchESM?: (url: string) => Promise + fetchESM?: (url: string) => Promise fetchCJS?: (url: string) => Promise constructor( defs: PluginDefinition[] = [], args?: { - fetchESM?: (url: string) => Promise + fetchESM?: (url: string) => Promise fetchCJS?: (url: string) => Promise }, ) { @@ -158,9 +158,11 @@ export default class PluginLoader { `cannot load plugins using protocol "${parsedUrl.protocol}"`, ) } - const plugin = (await this.fetchESM?.(parsedUrl.href)) as - | LoadedPlugin - | undefined + + if (!this.fetchESM) { + throw new Error(`No ESM fetcher installed`) + } + const plugin = await this.fetchESM(parsedUrl.href) if (!plugin) { throw new Error(`Could not load ESM plugin: ${parsedUrl}`) diff --git a/products/jbrowse-react-circular-genome-view/src/loadPlugins.ts b/products/jbrowse-react-circular-genome-view/src/loadPlugins.ts index 23faffafb4..4aedc8da79 100644 --- a/products/jbrowse-react-circular-genome-view/src/loadPlugins.ts +++ b/products/jbrowse-react-circular-genome-view/src/loadPlugins.ts @@ -1,4 +1,4 @@ -import PluginLoader from '@jbrowse/core/PluginLoader' +import PluginLoader, { LoadedPlugin } from '@jbrowse/core/PluginLoader' interface PluginDefinition { name: string @@ -8,7 +8,7 @@ interface PluginDefinition { export default async function loadPlugins( pluginDefinitions: PluginDefinition[], args?: { - fetchESM?: (url: string) => Promise + fetchESM?: (url: string) => Promise baseUrl?: string }, ) { diff --git a/products/jbrowse-react-linear-genome-view/src/loadPlugins.ts b/products/jbrowse-react-linear-genome-view/src/loadPlugins.ts index 23faffafb4..4aedc8da79 100644 --- a/products/jbrowse-react-linear-genome-view/src/loadPlugins.ts +++ b/products/jbrowse-react-linear-genome-view/src/loadPlugins.ts @@ -1,4 +1,4 @@ -import PluginLoader from '@jbrowse/core/PluginLoader' +import PluginLoader, { LoadedPlugin } from '@jbrowse/core/PluginLoader' interface PluginDefinition { name: string @@ -8,7 +8,7 @@ interface PluginDefinition { export default async function loadPlugins( pluginDefinitions: PluginDefinition[], args?: { - fetchESM?: (url: string) => Promise + fetchESM?: (url: string) => Promise baseUrl?: string }, ) {