@jbrowse/plugin-legacy-jbrowse 4.3.0 → 5.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/JBrowse1Connection/configSchema.d.ts +58 -18
- package/esm/JBrowse1Connection/configSchema.js +29 -3
- package/esm/JBrowse1Connection/get-value.d.ts +1 -10
- package/esm/JBrowse1Connection/get-value.js +9 -82
- package/esm/JBrowse1Connection/index.js +2 -2
- package/esm/JBrowse1Connection/jb1ConfigLoad.d.ts +9 -36
- package/esm/JBrowse1Connection/jb1ConfigLoad.js +72 -241
- package/esm/JBrowse1Connection/jb1ConfigParse.d.ts +6 -3
- package/esm/JBrowse1Connection/jb1ConfigParse.js +25 -250
- package/esm/JBrowse1Connection/jb1ToJb2.d.ts +8 -3
- package/esm/JBrowse1Connection/jb1ToJb2.js +124 -368
- package/esm/JBrowse1Connection/model.d.ts +79 -49
- package/esm/JBrowse1Connection/model.js +23 -9
- package/esm/JBrowse1Connection/set-value.d.ts +1 -0
- package/esm/JBrowse1Connection/set-value.js +14 -0
- package/esm/JBrowse1Connection/types.d.ts +4 -57
- package/esm/JBrowse1Connection/util.d.ts +13 -8
- package/esm/JBrowse1Connection/util.js +15 -80
- package/esm/JBrowse1TextSearchAdapter/HttpMap.d.ts +36 -8
- package/esm/JBrowse1TextSearchAdapter/HttpMap.js +61 -27
- package/esm/JBrowse1TextSearchAdapter/JBrowse1TextSearchAdapter.d.ts +5 -6
- package/esm/JBrowse1TextSearchAdapter/JBrowse1TextSearchAdapter.js +21 -23
- package/esm/JBrowse1TextSearchAdapter/configSchema.d.ts +36 -16
- package/esm/JBrowse1TextSearchAdapter/configSchema.js +28 -5
- package/esm/JBrowse1TextSearchAdapter/index.js +2 -2
- package/esm/NCListAdapter/NCListAdapter.d.ts +11 -1
- package/esm/NCListAdapter/NCListAdapter.js +16 -7
- package/esm/NCListAdapter/NCListFeature.d.ts +34 -3
- package/esm/NCListAdapter/NCListFeature.js +22 -8
- package/esm/NCListAdapter/configSchema.d.ts +31 -11
- package/esm/NCListAdapter/configSchema.js +26 -1
- package/esm/NCListAdapter/index.js +2 -2
- package/esm/index.js +3 -5
- package/package.json +9 -12
- package/esm/GuessNCList/index.d.ts +0 -2
- package/esm/GuessNCList/index.js +0 -15
|
@@ -1,27 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #config JBrowse1Connection
|
|
3
|
+
*
|
|
4
|
+
* #example
|
|
5
|
+
* An entry in the config's `connections`, pointing at a JBrowse 1 data
|
|
6
|
+
* directory — the one holding `trackList.json` and `seq/`. Its tracks are
|
|
7
|
+
* translated to JBrowse 2 equivalents on connect, which is the path for serving
|
|
8
|
+
* an existing JBrowse 1 instance's data without re-processing it.
|
|
9
|
+
* ```js
|
|
10
|
+
* {
|
|
11
|
+
* type: 'JBrowse1Connection',
|
|
12
|
+
* connectionId: 'jbrowse1_example',
|
|
13
|
+
* name: 'Legacy JBrowse 1 data',
|
|
14
|
+
* assemblyNames: ['hg19'],
|
|
15
|
+
* dataDirLocation: { uri: 'https://example.com/jbrowse1/data/' },
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
1
19
|
declare const JBrowse1Connection: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
20
|
+
assemblyNames: {
|
|
21
|
+
type: "stringArray";
|
|
22
|
+
} & {
|
|
23
|
+
defaultValue: unknown;
|
|
24
|
+
};
|
|
25
|
+
connectionId: {
|
|
26
|
+
type: "string";
|
|
27
|
+
} & {
|
|
28
|
+
defaultValue: string;
|
|
29
|
+
};
|
|
2
30
|
dataDirLocation: {
|
|
3
|
-
type:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
locationType: string;
|
|
7
|
-
};
|
|
8
|
-
description: string;
|
|
31
|
+
type: "fileLocation";
|
|
32
|
+
} & {
|
|
33
|
+
defaultValue: unknown;
|
|
9
34
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
defaultValue:
|
|
35
|
+
name: {
|
|
36
|
+
type: "string";
|
|
37
|
+
} & {
|
|
38
|
+
defaultValue: string;
|
|
39
|
+
};
|
|
40
|
+
type: {
|
|
41
|
+
type: "string";
|
|
42
|
+
} & {
|
|
43
|
+
defaultValue: string;
|
|
14
44
|
};
|
|
15
45
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
16
|
-
name: {
|
|
17
|
-
type: string;
|
|
46
|
+
readonly name: {
|
|
47
|
+
type: "string";
|
|
48
|
+
} & {
|
|
18
49
|
defaultValue: string;
|
|
19
|
-
description: string;
|
|
20
50
|
};
|
|
21
|
-
assemblyNames: {
|
|
22
|
-
type:
|
|
23
|
-
|
|
24
|
-
|
|
51
|
+
readonly assemblyNames: {
|
|
52
|
+
type: "stringArray";
|
|
53
|
+
} & {
|
|
54
|
+
defaultValue: unknown;
|
|
55
|
+
};
|
|
56
|
+
type: {
|
|
57
|
+
type: "string";
|
|
58
|
+
} & {
|
|
59
|
+
defaultValue: string;
|
|
60
|
+
};
|
|
61
|
+
connectionId: {
|
|
62
|
+
type: "string";
|
|
63
|
+
} & {
|
|
64
|
+
defaultValue: string;
|
|
25
65
|
};
|
|
26
|
-
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, "connectionId">>, undefined>>;
|
|
66
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, "connectionId", true, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>, any>>, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>, any>>;
|
|
27
67
|
export default JBrowse1Connection;
|
|
@@ -1,21 +1,47 @@
|
|
|
1
1
|
import { ConfigurationSchema } from '@jbrowse/core/configuration';
|
|
2
2
|
import { baseConnectionConfig } from '@jbrowse/core/pluggableElementTypes/models';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* #config JBrowse1Connection
|
|
5
|
+
*
|
|
6
|
+
* #example
|
|
7
|
+
* An entry in the config's `connections`, pointing at a JBrowse 1 data
|
|
8
|
+
* directory — the one holding `trackList.json` and `seq/`. Its tracks are
|
|
9
|
+
* translated to JBrowse 2 equivalents on connect, which is the path for serving
|
|
10
|
+
* an existing JBrowse 1 instance's data without re-processing it.
|
|
11
|
+
* ```js
|
|
12
|
+
* {
|
|
13
|
+
* type: 'JBrowse1Connection',
|
|
14
|
+
* connectionId: 'jbrowse1_example',
|
|
15
|
+
* name: 'Legacy JBrowse 1 data',
|
|
16
|
+
* assemblyNames: ['hg19'],
|
|
17
|
+
* dataDirLocation: { uri: 'https://example.com/jbrowse1/data/' },
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
4
21
|
const JBrowse1Connection = ConfigurationSchema('JBrowse1Connection', {
|
|
22
|
+
/**
|
|
23
|
+
* #slot
|
|
24
|
+
*/
|
|
5
25
|
dataDirLocation: {
|
|
6
26
|
type: 'fileLocation',
|
|
7
27
|
defaultValue: {
|
|
8
|
-
uri: '
|
|
28
|
+
uri: 'https://mysite.com/jbrowse/data/',
|
|
9
29
|
locationType: 'UriLocation',
|
|
10
30
|
},
|
|
11
|
-
description: 'the location of the JBrowse 1 data directory, often something like
|
|
31
|
+
description: 'the location of the JBrowse 1 data directory, often something like https://mysite.com/jbrowse/data/',
|
|
12
32
|
},
|
|
33
|
+
/**
|
|
34
|
+
* #slot
|
|
35
|
+
*/
|
|
13
36
|
assemblyNames: {
|
|
14
37
|
description: 'name of the assembly the connection belongs to, should be a single entry',
|
|
15
38
|
type: 'stringArray',
|
|
16
39
|
defaultValue: [],
|
|
17
40
|
},
|
|
18
41
|
}, {
|
|
42
|
+
/**
|
|
43
|
+
* #baseConfiguration
|
|
44
|
+
*/
|
|
19
45
|
baseConfiguration: baseConnectionConfig,
|
|
20
46
|
});
|
|
21
47
|
export default JBrowse1Connection;
|
|
@@ -1,10 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
default?: unknown;
|
|
3
|
-
separator?: string;
|
|
4
|
-
joinChar?: string;
|
|
5
|
-
join?: (segs: string[]) => string;
|
|
6
|
-
split?: (path: string) => string[];
|
|
7
|
-
isValid?: (key: string, target: {}) => boolean;
|
|
8
|
-
}
|
|
9
|
-
declare const getValue: (target: unknown, path: string | number | string[], options?: Options) => unknown;
|
|
10
|
-
export default getValue;
|
|
1
|
+
export default function getValue(target: Record<string, unknown>, path: string): unknown;
|
|
@@ -1,84 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
};
|
|
8
|
-
const split = (path, splitChar, options) => {
|
|
9
|
-
if (typeof options.split === 'function') {
|
|
10
|
-
return options.split(path);
|
|
11
|
-
}
|
|
12
|
-
return path.split(splitChar);
|
|
13
|
-
};
|
|
14
|
-
const isValid = (key, target = {}, options) => {
|
|
15
|
-
if (typeof options?.isValid === 'function') {
|
|
16
|
-
return options.isValid(key, target);
|
|
17
|
-
}
|
|
18
|
-
return true;
|
|
19
|
-
};
|
|
20
|
-
const isValidObject = (v) => {
|
|
21
|
-
return isObject(v) || typeof v === 'function';
|
|
22
|
-
};
|
|
23
|
-
const getValue = (target, path, options = {}) => {
|
|
24
|
-
if (!isObject(options)) {
|
|
25
|
-
options = { default: options };
|
|
26
|
-
}
|
|
27
|
-
if (!isValidObject(target)) {
|
|
28
|
-
return options.default !== undefined ? options.default : target;
|
|
29
|
-
}
|
|
30
|
-
if (typeof path === 'number') {
|
|
31
|
-
path = String(path);
|
|
32
|
-
}
|
|
33
|
-
const pathIsArray = Array.isArray(path);
|
|
34
|
-
const pathIsString = typeof path === 'string';
|
|
35
|
-
const splitChar = options.separator || '.';
|
|
36
|
-
const joinChar = options.joinChar || (typeof splitChar === 'string' ? splitChar : '.');
|
|
37
|
-
if (!pathIsString && !pathIsArray) {
|
|
38
|
-
return target;
|
|
39
|
-
}
|
|
40
|
-
if (target[path] !== undefined) {
|
|
41
|
-
return isValid(path, target, options) ? target[path] : options.default;
|
|
42
|
-
}
|
|
43
|
-
const segs = pathIsArray ? path : split(path, splitChar, options);
|
|
44
|
-
const len = segs.length;
|
|
45
|
-
let idx = 0;
|
|
46
|
-
do {
|
|
47
|
-
let prop = segs[idx];
|
|
48
|
-
if (typeof prop !== 'string') {
|
|
49
|
-
prop = String(prop);
|
|
50
|
-
}
|
|
51
|
-
while (prop.endsWith('\\')) {
|
|
52
|
-
prop = join([prop.slice(0, -1), segs[++idx] || ''], joinChar, options);
|
|
53
|
-
}
|
|
54
|
-
if (target[prop] !== undefined) {
|
|
55
|
-
if (!isValid(prop, target, options)) {
|
|
56
|
-
return options.default;
|
|
57
|
-
}
|
|
58
|
-
target = target[prop];
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
let hasProp = false;
|
|
62
|
-
let n = idx + 1;
|
|
63
|
-
while (n < len) {
|
|
64
|
-
prop = join([prop, segs[n++]], joinChar, options);
|
|
65
|
-
if ((hasProp = target[prop] !== undefined)) {
|
|
66
|
-
if (!isValid(prop, target, options)) {
|
|
67
|
-
return options.default;
|
|
68
|
-
}
|
|
69
|
-
target = target[prop];
|
|
70
|
-
idx = n - 1;
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (!hasProp) {
|
|
75
|
-
return options.default;
|
|
76
|
-
}
|
|
1
|
+
// minimal nested getter, mirroring ./set-value.ts
|
|
2
|
+
export default function getValue(target, path) {
|
|
3
|
+
let value = target;
|
|
4
|
+
for (const seg of path.split('.')) {
|
|
5
|
+
if (typeof value !== 'object' || value === null) {
|
|
6
|
+
return undefined;
|
|
77
7
|
}
|
|
78
|
-
|
|
79
|
-
if (idx === len) {
|
|
80
|
-
return target;
|
|
8
|
+
value = value[seg];
|
|
81
9
|
}
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
export default getValue;
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ConnectionType from '@jbrowse/core/pluggableElementTypes/ConnectionType';
|
|
2
|
-
import configSchema from
|
|
3
|
-
import modelFactory from
|
|
2
|
+
import configSchema from './configSchema.js';
|
|
3
|
+
import modelFactory from './model.js';
|
|
4
4
|
export default function JBrowse1ConnectionF(pluginManager) {
|
|
5
5
|
pluginManager.addConnectionType(() => new ConnectionType({
|
|
6
6
|
name: 'JBrowse1Connection',
|
|
@@ -1,36 +1,9 @@
|
|
|
1
|
-
import type { Config
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
show_overview: boolean;
|
|
11
|
-
show_fullviewlink: boolean;
|
|
12
|
-
update_browser_title: boolean;
|
|
13
|
-
updateBrowserURL: boolean;
|
|
14
|
-
refSeqs: string;
|
|
15
|
-
include: string[];
|
|
16
|
-
nameUrl: string;
|
|
17
|
-
datasets: {
|
|
18
|
-
_DEFAULT_EXAMPLES: boolean;
|
|
19
|
-
volvox: {
|
|
20
|
-
url: string;
|
|
21
|
-
name: string;
|
|
22
|
-
};
|
|
23
|
-
modencode: {
|
|
24
|
-
url: string;
|
|
25
|
-
name: string;
|
|
26
|
-
};
|
|
27
|
-
yeast: {
|
|
28
|
-
url: string;
|
|
29
|
-
name: string;
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
highlightSearchedRegions: boolean;
|
|
33
|
-
highResolutionMode: string;
|
|
34
|
-
}): Promise<Config>;
|
|
35
|
-
export declare function fetchConfigFile(location: JBLocation): Promise<Config>;
|
|
36
|
-
export declare function parseJb1(config: string, url?: string): Config;
|
|
1
|
+
import type { Config } from './types.ts';
|
|
2
|
+
import type { FileLocation } from '@jbrowse/core/util/types';
|
|
3
|
+
/**
|
|
4
|
+
* Read the track list out of a JBrowse 1 data directory. Only the tracks and
|
|
5
|
+
* the data root survive: the rest of a JBrowse 1 config — display defaults,
|
|
6
|
+
* `refSeqs`, `nameUrl`, the synthesized `stores` — describes the JBrowse 1
|
|
7
|
+
* browser, which is not the thing being configured here.
|
|
8
|
+
*/
|
|
9
|
+
export declare function fetchJb1(dataDir: FileLocation): Promise<Config>;
|
|
@@ -1,247 +1,78 @@
|
|
|
1
1
|
import { openLocation } from '@jbrowse/core/util/io';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { parseJb1 } from './jb1ConfigParse.js';
|
|
3
|
+
import { fillTemplate, isTrack } from './util.js';
|
|
4
|
+
// JBrowse 1 kept a data directory's track list in either or both of these, so
|
|
5
|
+
// a missing one is normal rather than an error
|
|
6
|
+
const TRACK_LISTS = ['trackList.json', 'tracks.conf'];
|
|
7
|
+
/**
|
|
8
|
+
* Read the track list out of a JBrowse 1 data directory. Only the tracks and
|
|
9
|
+
* the data root survive: the rest of a JBrowse 1 config — display defaults,
|
|
10
|
+
* `refSeqs`, `nameUrl`, the synthesized `stores` — describes the JBrowse 1
|
|
11
|
+
* browser, which is not the thing being configured here.
|
|
12
|
+
*/
|
|
13
|
+
export async function fetchJb1(dataDir) {
|
|
14
|
+
const dataRoot = locationPath(dataDir).replace(/\/$/, '');
|
|
15
|
+
const tracks = [];
|
|
16
|
+
const seen = new Set();
|
|
17
|
+
async function read(path) {
|
|
18
|
+
if (seen.has(path)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
seen.add(path);
|
|
22
|
+
const config = await readConfig(dataDir, path);
|
|
23
|
+
if (!config) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
tracks.push(...regularizeTracks(config.tracks, { dataRoot }));
|
|
27
|
+
const dir = path.replace(/\/[^/]*$/, '');
|
|
28
|
+
for (const include of [config.include ?? []].flat()) {
|
|
29
|
+
await read(/^\w[\w+.-]*:/.test(include) ? include : `${dir}/${include}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
for (const name of TRACK_LISTS) {
|
|
33
|
+
await read(`${dataRoot}/${name}`);
|
|
34
|
+
}
|
|
35
|
+
return { dataRoot, tracks };
|
|
6
36
|
}
|
|
7
|
-
function
|
|
8
|
-
return
|
|
37
|
+
function locationPath(location) {
|
|
38
|
+
return 'uri' in location
|
|
39
|
+
? location.uri
|
|
40
|
+
: 'localPath' in location
|
|
41
|
+
? location.localPath
|
|
42
|
+
: '';
|
|
9
43
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (dataRootLocation.endsWith('/')) {
|
|
23
|
-
dataRootReg[protocol] = dataRootLocation.slice(0, -1);
|
|
24
|
-
}
|
|
25
|
-
if ((isUriLocation(baseConfigRoot) && baseConfigRoot.uri) ||
|
|
26
|
-
(isLocalPathLocation(baseConfigRoot) && baseConfigRoot.localPath)) {
|
|
27
|
-
const baseProtocol = 'uri' in baseConfigRoot ? 'uri' : 'localPath';
|
|
28
|
-
let baseConfigLocation = '';
|
|
29
|
-
if (isUriLocation(baseConfigRoot)) {
|
|
30
|
-
baseConfigLocation = baseConfigRoot.uri;
|
|
31
|
-
}
|
|
32
|
-
if (isLocalPathLocation(baseConfigRoot)) {
|
|
33
|
-
baseConfigLocation = baseConfigRoot.localPath;
|
|
34
|
-
}
|
|
35
|
-
if (baseConfigLocation.endsWith('/')) {
|
|
36
|
-
baseConfigLocation = baseConfigLocation.slice(0, -1);
|
|
37
|
-
}
|
|
38
|
-
let newConfig = {};
|
|
39
|
-
for (const conf of ['jbrowse.conf', 'jbrowse_conf.json']) {
|
|
40
|
-
let fetchedConfig = null;
|
|
41
|
-
try {
|
|
42
|
-
fetchedConfig = await fetchConfigFile({
|
|
43
|
-
[baseProtocol]: `${baseConfigLocation}/${conf}`,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
console.error(`tried to access ${baseConfigLocation}/${conf}, but failed`);
|
|
48
|
-
}
|
|
49
|
-
newConfig = mergeConfigs(newConfig, fetchedConfig) || {};
|
|
50
|
-
}
|
|
51
|
-
if (dataRootReg[protocol]) {
|
|
52
|
-
newConfig.dataRoot = dataRootReg[protocol];
|
|
53
|
-
}
|
|
54
|
-
return createFinalConfig(newConfig);
|
|
55
|
-
}
|
|
56
|
-
const newConfig = regularizeConf(baseConfig, window.location.href);
|
|
57
|
-
if (dataRootReg[protocol]) {
|
|
58
|
-
newConfig.dataRoot = dataRootReg[protocol];
|
|
59
|
-
}
|
|
60
|
-
return createFinalConfig(newConfig);
|
|
61
|
-
}
|
|
62
|
-
export async function createFinalConfig(baseConfig, defaults = configDefaults) {
|
|
63
|
-
const configWithDefaults = deepUpdate(structuredClone(defaults), baseConfig);
|
|
64
|
-
let finalConfig = await loadIncludes(configWithDefaults);
|
|
65
|
-
finalConfig = mergeConfigs(finalConfig, baseConfig) || finalConfig;
|
|
66
|
-
fillTemplates(finalConfig, finalConfig);
|
|
67
|
-
validateConfig(finalConfig);
|
|
68
|
-
return finalConfig;
|
|
69
|
-
}
|
|
70
|
-
export async function fetchConfigFile(location) {
|
|
71
|
-
const result = await openLocation(location).readFile('utf8');
|
|
72
|
-
if (isUriLocation(location)) {
|
|
73
|
-
return parseJb1(result, location.uri);
|
|
74
|
-
}
|
|
75
|
-
if (isLocalPathLocation(location)) {
|
|
76
|
-
return parseJb1(result, location.localPath);
|
|
77
|
-
}
|
|
78
|
-
return parseJb1(result);
|
|
79
|
-
}
|
|
80
|
-
export function parseJb1(config, url = '') {
|
|
81
|
-
if (config.trim().startsWith('{')) {
|
|
82
|
-
return parseJB1Json(config, url);
|
|
83
|
-
}
|
|
84
|
-
return parseJB1Conf(config, url);
|
|
44
|
+
async function readConfig(dataDir, path) {
|
|
45
|
+
const location = 'localPath' in dataDir
|
|
46
|
+
? { localPath: path, locationType: 'LocalPathLocation' }
|
|
47
|
+
: { uri: path, locationType: 'UriLocation' };
|
|
48
|
+
let text;
|
|
49
|
+
try {
|
|
50
|
+
text = await openLocation(location).readFile('utf8');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return parseJb1(text, path);
|
|
85
56
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
typeof b[prop] === 'object' &&
|
|
107
|
-
typeof a[prop] === 'object') {
|
|
108
|
-
a[prop] = deepUpdate(a[prop], b[prop]);
|
|
109
|
-
}
|
|
110
|
-
else if (prop === 'dataRoot') {
|
|
111
|
-
if (a[prop] === undefined ||
|
|
112
|
-
(a[prop] === 'data' && b[prop] !== undefined)) {
|
|
113
|
-
a[prop] = b[prop];
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
else if (a[prop] === undefined || b[prop] !== undefined) {
|
|
117
|
-
a[prop] = b[prop];
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return a;
|
|
121
|
-
}
|
|
122
|
-
function mergeTrackConfigs(a, b) {
|
|
123
|
-
if (!b.length) {
|
|
124
|
-
return a;
|
|
125
|
-
}
|
|
126
|
-
const aTracks = {};
|
|
127
|
-
for (const [i, t] of a.entries()) {
|
|
128
|
-
t.index = i;
|
|
129
|
-
aTracks[t.label] = t;
|
|
130
|
-
}
|
|
131
|
-
for (const bT of b) {
|
|
132
|
-
const aT = aTracks[bT.label];
|
|
133
|
-
if (aT) {
|
|
134
|
-
mergeConfigs(aT, bT);
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
a.push(bT);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return a;
|
|
141
|
-
}
|
|
142
|
-
async function loadIncludes(inputConfig) {
|
|
143
|
-
inputConfig = structuredClone(inputConfig);
|
|
144
|
-
async function loadRecur(config, upstreamConf) {
|
|
145
|
-
const sourceUrl = config.sourceUrl || config.baseUrl;
|
|
146
|
-
if (!sourceUrl) {
|
|
147
|
-
throw new Error(`Could not determine source URL: ${JSON.stringify(config)}`);
|
|
148
|
-
}
|
|
149
|
-
const newUpstreamConf = mergeConfigs(structuredClone(upstreamConf), config);
|
|
150
|
-
if (!newUpstreamConf) {
|
|
151
|
-
throw new Error('Problem merging configs');
|
|
152
|
-
}
|
|
153
|
-
const includes = fillTemplates(regularizeIncludes(config.include || []), newUpstreamConf);
|
|
154
|
-
config.include = undefined;
|
|
155
|
-
const loads = includes.map(async (include) => {
|
|
156
|
-
include.cacheBuster = inputConfig.cacheBuster;
|
|
157
|
-
const includedData = await fetchConfigFile({
|
|
158
|
-
uri: new URL(include.url, sourceUrl).href,
|
|
159
|
-
locationType: 'UriLocation',
|
|
160
|
-
});
|
|
161
|
-
return loadRecur(includedData, newUpstreamConf);
|
|
162
|
-
});
|
|
163
|
-
const includedDataObjects = await Promise.all(loads);
|
|
164
|
-
for (const includedData of includedDataObjects) {
|
|
165
|
-
config = mergeConfigs(config, includedData) || config;
|
|
166
|
-
}
|
|
167
|
-
return config;
|
|
168
|
-
}
|
|
169
|
-
return loadRecur(inputConfig, {});
|
|
170
|
-
}
|
|
171
|
-
function regularizeIncludes(includes) {
|
|
172
|
-
if (!includes) {
|
|
173
|
-
return [];
|
|
174
|
-
}
|
|
175
|
-
if (!Array.isArray(includes)) {
|
|
176
|
-
includes = [includes];
|
|
177
|
-
}
|
|
178
|
-
return includes.map((include) => {
|
|
179
|
-
if (typeof include === 'string') {
|
|
180
|
-
include = { url: include };
|
|
181
|
-
}
|
|
182
|
-
if (!('format' in include)) {
|
|
183
|
-
include.format = include.url.endsWith('.conf') ? 'conf' : 'JB_json';
|
|
184
|
-
}
|
|
185
|
-
if (include.format === 'JB_json' && !('version' in include)) {
|
|
186
|
-
include.version = 1;
|
|
187
|
-
}
|
|
188
|
-
return include;
|
|
57
|
+
/**
|
|
58
|
+
* JBrowse 1 accepted `tracks` as an array, as a single track, or as an object
|
|
59
|
+
* keyed by label; let a track hold its settings in a `config` subobject; and
|
|
60
|
+
* let any string value interpolate `{dataRoot}`.
|
|
61
|
+
*/
|
|
62
|
+
function regularizeTracks(tracks, fillWith) {
|
|
63
|
+
const list = Array.isArray(tracks)
|
|
64
|
+
? tracks
|
|
65
|
+
: !tracks
|
|
66
|
+
? []
|
|
67
|
+
: isTrack(tracks)
|
|
68
|
+
? [tracks]
|
|
69
|
+
: Object.entries(tracks).map(([label, track]) => ({ label, ...track }));
|
|
70
|
+
return list.map(track => {
|
|
71
|
+
const { config, ...rest } = track;
|
|
72
|
+
const flat = { ...config, ...rest };
|
|
73
|
+
return Object.fromEntries(Object.entries(flat).map(([key, value]) => [
|
|
74
|
+
key,
|
|
75
|
+
typeof value === 'string' ? fillTemplate(value, fillWith) : value,
|
|
76
|
+
]));
|
|
189
77
|
});
|
|
190
78
|
}
|
|
191
|
-
function fillTemplates(subconfig, config) {
|
|
192
|
-
if (!subconfig) {
|
|
193
|
-
return subconfig;
|
|
194
|
-
}
|
|
195
|
-
if (Array.isArray(subconfig)) {
|
|
196
|
-
for (let i = 0; i < subconfig.length; i += 1) {
|
|
197
|
-
subconfig[i] = fillTemplates(subconfig[i], config);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
else if (typeof subconfig === 'object') {
|
|
201
|
-
const sub = subconfig;
|
|
202
|
-
for (const name of Object.keys(sub)) {
|
|
203
|
-
sub[name] = fillTemplates(sub[name], config);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
else if (typeof subconfig === 'string') {
|
|
207
|
-
return fillTemplate(subconfig, config);
|
|
208
|
-
}
|
|
209
|
-
return subconfig;
|
|
210
|
-
}
|
|
211
|
-
function noRecursiveMerge(propName) {
|
|
212
|
-
return propName === 'datasets';
|
|
213
|
-
}
|
|
214
|
-
const configDefaults = {
|
|
215
|
-
tracks: [],
|
|
216
|
-
containerID: 'GenomeBrowser',
|
|
217
|
-
dataRoot: 'data',
|
|
218
|
-
show_tracklist: true,
|
|
219
|
-
show_nav: true,
|
|
220
|
-
show_menu: true,
|
|
221
|
-
show_overview: true,
|
|
222
|
-
show_fullviewlink: true,
|
|
223
|
-
update_browser_title: true,
|
|
224
|
-
updateBrowserURL: true,
|
|
225
|
-
refSeqs: '{dataRoot}/seq/refSeqs.json',
|
|
226
|
-
include: ['jbrowse.conf', 'jbrowse_conf.json'],
|
|
227
|
-
nameUrl: '{dataRoot}/names/root.json',
|
|
228
|
-
datasets: {
|
|
229
|
-
_DEFAULT_EXAMPLES: true,
|
|
230
|
-
volvox: { url: '?data=sample_data/json/volvox', name: 'Volvox Example' },
|
|
231
|
-
modencode: {
|
|
232
|
-
url: '?data=sample_data/json/modencode',
|
|
233
|
-
name: 'MODEncode Example',
|
|
234
|
-
},
|
|
235
|
-
yeast: { url: '?data=sample_data/json/yeast', name: 'Yeast Example' },
|
|
236
|
-
},
|
|
237
|
-
highlightSearchedRegions: false,
|
|
238
|
-
highResolutionMode: 'auto',
|
|
239
|
-
};
|
|
240
|
-
function validateConfig(config) {
|
|
241
|
-
if (!config.tracks) {
|
|
242
|
-
config.tracks = [];
|
|
243
|
-
}
|
|
244
|
-
if (!config.baseUrl) {
|
|
245
|
-
throw new Error('Must provide a `baseUrl` in configuration');
|
|
246
|
-
}
|
|
247
|
-
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { Config } from './types.ts';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Parse a JBrowse 1 config file. A `trackList.json` is JSON; a `tracks.conf`
|
|
4
|
+
* is JBrowse 1's own INI-like format, whose `[section.subsection]` headers and
|
|
5
|
+
* `key.subkey = value` lines both address a dotted path.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseJb1(text: string, url?: string): Config;
|