@jbrowse/cli 5.0.0-beta.10 → 5.0.0-beta.13
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/README.md +6 -6
- package/dist/commands/add-track-utils/adapter-utils.js +1 -9
- package/dist/commands/add-track.js +3 -3
- package/dist/commands/make-density/index.js +1 -1
- package/dist/commands/validate/configManifest.generated.js +236 -67
- package/dist/commands/validate/configSchema.generated.js +8632 -2974
- package/dist/commands/validate/index.js +1 -1
- package/dist/commands/validate/markRules/colorScale.js +33 -2
- package/dist/commands/validate/markRules/markProblems.js +62 -21
- package/dist/commands/validate/markRules/markSpecs.js +6 -18
- package/dist/commands/validate/markRules/markVocabulary.js +3 -1
- package/dist/commands/validate/schemaValidate.js +2 -2
- package/dist/commands/validate/validateConfig.js +25 -24
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -24,7 +24,7 @@ what it cannot draw in a corner notice rather than refusing the track.
|
|
|
24
24
|
This command runs the display's own rule list over every "marks" list in the
|
|
25
25
|
file: an error where a mark draws nothing, never draws or a step cannot run,
|
|
26
26
|
and a warning where a slot waits unread or the marks draw in an arrangement
|
|
27
|
-
the author may not have meant. With --json each finding
|
|
27
|
+
the author may not have meant. With --json each finding has the stable
|
|
28
28
|
"rule" id of the rule that made it.
|
|
29
29
|
|
|
30
30
|
Types registered by plugins are not known to this command, so they come through
|
|
@@ -21,8 +21,8 @@ export const IDENTITY_SCALE = 'identity';
|
|
|
21
21
|
const CATEGORICAL_PRESET = { scale: 'categorical' };
|
|
22
22
|
/**
|
|
23
23
|
* The fields whose values have names, an order and colours of their own on
|
|
24
|
-
* every display, beneath each display's own presets.
|
|
25
|
-
*
|
|
24
|
+
* every display, beneath each display's own presets. The synteny ribbons
|
|
25
|
+
* paint strand from this range too. The SV classes are
|
|
26
26
|
* `svClassOf`'s, deletion red and duplication blue as dbVar and gnomAD-SV
|
|
27
27
|
* paint them, insertion the pileup's purple, and the rest kept apart under
|
|
28
28
|
* deuteranopia and protanopia. A record with no class files under `''`, so
|
|
@@ -166,6 +166,37 @@ export function scaleEndProblems({ domainMin, domainMax, domainQuantile }, { rev
|
|
|
166
166
|
export function noticeLines(setting, problems) {
|
|
167
167
|
return problems.map(({ slot, message }) => `${setting}.${slot}: ${message}`);
|
|
168
168
|
}
|
|
169
|
+
function numberOf(value) {
|
|
170
|
+
return typeof value === 'number' ? value : undefined;
|
|
171
|
+
}
|
|
172
|
+
function stringOf(value) {
|
|
173
|
+
return typeof value === 'string' ? value : undefined;
|
|
174
|
+
}
|
|
175
|
+
function listOf(value) {
|
|
176
|
+
return Array.isArray(value) ? value : undefined;
|
|
177
|
+
}
|
|
178
|
+
/** A quantitative scale's ends among the members `member` reads, each a number or unset. */
|
|
179
|
+
export function scaleEndsOf(member) {
|
|
180
|
+
return {
|
|
181
|
+
domainMin: numberOf(member('domainMin')),
|
|
182
|
+
domainMax: numberOf(member('domainMax')),
|
|
183
|
+
domainQuantile: numberOf(member('domainQuantile')),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The members `colorProblems` reads among the ones `member` reads, each of the
|
|
188
|
+
* type it reads or unset, so a config snapshot and a config node answer alike.
|
|
189
|
+
*/
|
|
190
|
+
export function colorSlotsOf(member) {
|
|
191
|
+
return {
|
|
192
|
+
...scaleEndsOf(member),
|
|
193
|
+
field: stringOf(member('field')) ?? '',
|
|
194
|
+
scale: stringOf(member('scale')),
|
|
195
|
+
domain: listOf(member('domain')),
|
|
196
|
+
range: listOf(member('range')),
|
|
197
|
+
labels: listOf(member('labels')),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
169
200
|
function pinned(entry) {
|
|
170
201
|
return entry !== '' && Number.isFinite(Number(entry));
|
|
171
202
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Generated by scripts/generateMarkRules.ts from plugins/marks/src/LinearMarkDisplay/markProblems.ts: do not edit, `pnpm autogen` rewrites it.
|
|
2
2
|
import { AUTO_BIN } from './autoBin.js';
|
|
3
3
|
import { FEATURE_FIELD_PRESETS, aggregateFieldName, colorProblems, fieldScaleOf, isJexl, SHAPE_NAMES, isNamedColor, paintedScale, scaleEndProblems, universalPresetOf, } from './markRuleFacts.js';
|
|
4
|
-
import { MARK_SPECS,
|
|
5
|
-
import { DEFAULT_AGGREGATE_OP, BIN_OVERLAP_FIELD, DEFAULT_BIN_AS, DEFAULT_BIN_FIELD, DEFAULT_COVERAGE_AS, DEFAULT_FORMULA_AS, DEFAULT_LINE_INTERPOLATE, DEFAULT_ROW_PROPORTION, DEFAULT_LINK_SHAPE, DEFAULT_MARK_TYPE, CELLS_FIELDS, DEFAULT_PILEUP_AS, DEFAULT_PILEUP_FIELDS, DEFAULT_TEXT_FIELD, DEFAULT_X2, MATE_FIELDS, } from './markVocabulary.js';
|
|
4
|
+
import { MARK_SPECS, readsValue } from './markSpecs.js';
|
|
5
|
+
import { DEFAULT_AGGREGATE_OP, BIN_OVERLAP_FIELD, DEFAULT_BIN_AS, DEFAULT_BIN_FIELD, DEFAULT_COVERAGE_AS, DEFAULT_FORMULA_AS, DEFAULT_LINE_INTERPOLATE, DEFAULT_ROW_PROPORTION, DEFAULT_LINK_SHAPE, DEFAULT_MARK_TYPE, CELLS_FIELDS, DEFAULT_PILEUP_AS, DEFAULT_PILEUP_FIELDS, DEFAULT_TEXT_FIELD, DEFAULT_X2, MATE_FIELDS, MAX_THRESHOLD_CUTS, } from './markVocabulary.js';
|
|
6
6
|
import { stepChannels } from './stepChannels.js';
|
|
7
7
|
/**
|
|
8
8
|
* The level of every rule of the list, by the stable id a report and a test
|
|
@@ -27,6 +27,8 @@ export const MARK_RULES = {
|
|
|
27
27
|
'threshold-cuts': 'warning',
|
|
28
28
|
/** A threshold colour naming no cut, so every value paints one colour. */
|
|
29
29
|
'threshold-no-cuts': 'warning',
|
|
30
|
+
/** A threshold colour naming more cuts than it paints, so a value past the eighth takes the ninth colour. */
|
|
31
|
+
'threshold-cut-count': 'warning',
|
|
30
32
|
/** A threshold `range` not one colour longer than its cuts. */
|
|
31
33
|
'threshold-range': 'warning',
|
|
32
34
|
/** A `domain` on a linear or log colour, whose ends are `domainMin` and `domainMax`. */
|
|
@@ -39,8 +41,6 @@ export const MARK_RULES = {
|
|
|
39
41
|
'field-spells-constant': 'warning',
|
|
40
42
|
/** A colour's or a shape's `labels` naming values its `domain` does not list, or no categorical scale's. */
|
|
41
43
|
'labels-domain': 'warning',
|
|
42
|
-
/** A text's colour ramp with an open end, whose colours then differ from one region to the next. */
|
|
43
|
-
'unpinned-text-ramp': 'warning',
|
|
44
44
|
/** A `minBpPerPx` not below the mark's `maxBpPerPx`, so the mark never draws. */
|
|
45
45
|
'empty-zoom-range': 'error',
|
|
46
46
|
/** A `filter` or `formula` whose `expr` is not a `jexl:` expression. */
|
|
@@ -59,6 +59,8 @@ export const MARK_RULES = {
|
|
|
59
59
|
'step-field-expression': 'error',
|
|
60
60
|
/** A `y` naming a field that no `aggregate` or `coverage` step before it writes. */
|
|
61
61
|
'unwritten-y': 'error',
|
|
62
|
+
/** Another channel naming a field that no `aggregate` or `coverage` step before it writes, so it reads no value; the field a `facet` or `rows` splits on counts as written. */
|
|
63
|
+
'unwritten-field': 'warning',
|
|
62
64
|
/** A mark other than a span drawn beside one that stacks rows, standing in the first of them. */
|
|
63
65
|
'value-beside-rows': 'warning',
|
|
64
66
|
/** Two `pileup` steps packing one plot, whose rows share numbers. */
|
|
@@ -120,11 +122,6 @@ function stepsOf(mark) {
|
|
|
120
122
|
function colorScaleOf({ scale, field = '' }) {
|
|
121
123
|
return paintedScale({ scale, field }, fieldScaleOf(FEATURE_FIELD_PRESETS, field));
|
|
122
124
|
}
|
|
123
|
-
function rampColor(mark) {
|
|
124
|
-
const color = mark.encoding?.color ?? {};
|
|
125
|
-
const painted = colorScaleOf(color);
|
|
126
|
-
return painted === 'linear' || painted === 'log' ? color : undefined;
|
|
127
|
-
}
|
|
128
125
|
function sizeFieldOf(size) {
|
|
129
126
|
return typeof size === 'string'
|
|
130
127
|
? size
|
|
@@ -381,7 +378,34 @@ function stepProblems(steps, list = 'transform') {
|
|
|
381
378
|
}
|
|
382
379
|
return problems;
|
|
383
380
|
}
|
|
384
|
-
|
|
381
|
+
// Each field a mark's channels read besides `y`, by the slot naming it: the
|
|
382
|
+
// channels its type reads, a colour's or a shape's field where it is a scale,
|
|
383
|
+
// a text's field as its default leaves it, and a link's size field.
|
|
384
|
+
function channelFields(mark, spec) {
|
|
385
|
+
const encoding = mark.encoding ?? {};
|
|
386
|
+
const { x, x2, row, color = {}, shape, text = DEFAULT_TEXT_FIELD } = encoding;
|
|
387
|
+
const reads = (channel) => spec.channels.includes(channel);
|
|
388
|
+
const named = [
|
|
389
|
+
['encoding.x', x],
|
|
390
|
+
['encoding.x2', typeof x2 === 'string' ? x2 : x2?.pos],
|
|
391
|
+
['encoding.x2.chrom', typeof x2 === 'object' ? x2.chrom : undefined],
|
|
392
|
+
['encoding.row', reads('row') ? row : undefined],
|
|
393
|
+
[
|
|
394
|
+
'encoding.color.field',
|
|
395
|
+
reads('color') && colorScaleOf(color) !== 'none'
|
|
396
|
+
? fieldOf(color)
|
|
397
|
+
: undefined,
|
|
398
|
+
],
|
|
399
|
+
['encoding.shape.field', reads('shape') ? fieldOf(shape) : undefined],
|
|
400
|
+
['encoding.text', reads('text') ? text : undefined],
|
|
401
|
+
[
|
|
402
|
+
'encoding.size.field',
|
|
403
|
+
spec.size === 'channel' ? sizeFieldOf(encoding.size) : undefined,
|
|
404
|
+
],
|
|
405
|
+
];
|
|
406
|
+
return named.flatMap(([slot, field]) => field && !isJexl(field) ? [[slot, field]] : []);
|
|
407
|
+
}
|
|
408
|
+
function ownProblems(mark, display, section, split) {
|
|
385
409
|
const type = markTypeOf(mark);
|
|
386
410
|
const problems = [];
|
|
387
411
|
const y = mark.encoding?.y;
|
|
@@ -430,6 +454,10 @@ function ownProblems(mark, display, section) {
|
|
|
430
454
|
(color.domain ?? []).length === 0) {
|
|
431
455
|
problems.push(found('threshold-no-cuts', 'encoding.color.domain', 'a threshold names its cut points in domain, and with none every value paints the first colour'));
|
|
432
456
|
}
|
|
457
|
+
const cutCount = (color.domain ?? []).filter(cut => Number.isFinite(Number(cut))).length;
|
|
458
|
+
if (colorScaleOf(color) === 'threshold' && cutCount > MAX_THRESHOLD_CUTS) {
|
|
459
|
+
problems.push(found('threshold-cut-count', 'encoding.color.domain', `a threshold paints ${MAX_THRESHOLD_CUTS} cuts, and these ${cutCount} leave a value past the ${MAX_THRESHOLD_CUTS}th in the ${MAX_THRESHOLD_CUTS + 1}th colour`));
|
|
460
|
+
}
|
|
433
461
|
problems.push(...shapeLabelProblems(mark.encoding?.shape));
|
|
434
462
|
problems.push(...constantAsFieldProblems(mark.encoding));
|
|
435
463
|
if (typeof size === 'object') {
|
|
@@ -437,14 +465,6 @@ function ownProblems(mark, display, section) {
|
|
|
437
465
|
problems.push(found(rule, `encoding.size.${slot}`, message));
|
|
438
466
|
}
|
|
439
467
|
}
|
|
440
|
-
const ramp = rampColor(mark);
|
|
441
|
-
if (ramp) {
|
|
442
|
-
const { domainMin, domainMax } = ramp;
|
|
443
|
-
if (rampResolvesPerRegion(type) &&
|
|
444
|
-
(domainMin === undefined || domainMax === undefined)) {
|
|
445
|
-
problems.push(found('unpinned-text-ramp', `encoding.color.${domainMin === undefined ? 'domainMin' : 'domainMax'}`, `a ${type}'s ramp resolves an open end against each region's own extremes, so its colours agree across regions only with domainMin and domainMax both pinned`));
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
468
|
const { minBpPerPx = 0, maxBpPerPx = 0 } = mark;
|
|
449
469
|
if (minBpPerPx > 0 && maxBpPerPx > 0 && minBpPerPx >= maxBpPerPx) {
|
|
450
470
|
problems.push(found('empty-zoom-range', 'minBpPerPx', `never draws: minBpPerPx ${minBpPerPx} is not below maxBpPerPx ${maxBpPerPx}`));
|
|
@@ -454,8 +474,19 @@ function ownProblems(mark, display, section) {
|
|
|
454
474
|
problems.push(found('two-packings', 'transform', `packs rows of its own over the ${packsIn(section) ? "facet's" : "display's"} pileup, and the two share row numbers; one pileup packs every mark`));
|
|
455
475
|
}
|
|
456
476
|
const fields = madeFields([...display, ...section, ...stepsOf(mark)]);
|
|
457
|
-
if (fields
|
|
458
|
-
|
|
477
|
+
if (fields) {
|
|
478
|
+
if (split) {
|
|
479
|
+
fields.add(split);
|
|
480
|
+
}
|
|
481
|
+
const leaves = `which no step before it writes; they leave ${[...fields].join(', ')}`;
|
|
482
|
+
if (y && !isJexl(y) && !fields.has(y)) {
|
|
483
|
+
problems.push(found('unwritten-y', 'encoding.y', `reads "${y}", ${leaves}`));
|
|
484
|
+
}
|
|
485
|
+
for (const [slot, field] of channelFields(mark, spec)) {
|
|
486
|
+
if (!fields.has(field)) {
|
|
487
|
+
problems.push(found('unwritten-field', slot, `reads "${field}", ${leaves}`));
|
|
488
|
+
}
|
|
489
|
+
}
|
|
459
490
|
}
|
|
460
491
|
return problems;
|
|
461
492
|
}
|
|
@@ -468,6 +499,13 @@ function ownProblems(mark, display, section) {
|
|
|
468
499
|
export function markProblems({ marks, facet, transform = [], rows, scales, }) {
|
|
469
500
|
const faceted = named(facet?.field);
|
|
470
501
|
const drawsRows = named(rows?.field) && !faceted;
|
|
502
|
+
// the field the sections or rows are split on, which every section's rows
|
|
503
|
+
// keep through the steps that make rows
|
|
504
|
+
const split = faceted
|
|
505
|
+
? String(facet?.field)
|
|
506
|
+
: drawsRows
|
|
507
|
+
? String(rows?.field)
|
|
508
|
+
: undefined;
|
|
471
509
|
const section = readable(facet?.transform ?? []);
|
|
472
510
|
const display = readable(transform);
|
|
473
511
|
const shared = [...display, ...section];
|
|
@@ -489,7 +527,10 @@ export function markProblems({ marks, facet, transform = [], rows, scales, }) {
|
|
|
489
527
|
: []),
|
|
490
528
|
...(drawsRows ? packingsUnderRows(marks, transform, section) : []),
|
|
491
529
|
...marks.flatMap((mark, i) => mark
|
|
492
|
-
? ownProblems(mark, display, section).map(p => ({
|
|
530
|
+
? ownProblems(mark, display, section, split).map(p => ({
|
|
531
|
+
mark: i,
|
|
532
|
+
...p,
|
|
533
|
+
}))
|
|
493
534
|
: []),
|
|
494
535
|
];
|
|
495
536
|
for (const [i, mark] of marks.entries()) {
|
|
@@ -12,46 +12,39 @@ export const MARK_SPECS = {
|
|
|
12
12
|
bar: {
|
|
13
13
|
channels: ['y', 'row', 'color'],
|
|
14
14
|
value: 'required',
|
|
15
|
-
ramp: 'display',
|
|
16
15
|
hit: 'rows',
|
|
17
16
|
},
|
|
18
17
|
point: {
|
|
19
18
|
channels: ['y', 'row', 'color', 'shape'],
|
|
20
19
|
value: 'required',
|
|
21
|
-
ramp: 'display',
|
|
22
20
|
hit: 'index',
|
|
23
21
|
size: 'constant',
|
|
24
22
|
},
|
|
25
23
|
rule: {
|
|
26
24
|
channels: ['y', 'row', 'color'],
|
|
27
25
|
value: 'required',
|
|
28
|
-
ramp: 'display',
|
|
29
26
|
hit: 'rows',
|
|
30
27
|
size: 'constant',
|
|
31
28
|
},
|
|
32
29
|
line: {
|
|
33
30
|
channels: ['y', 'row', 'color'],
|
|
34
31
|
value: 'required',
|
|
35
|
-
ramp: 'display',
|
|
36
32
|
hit: 'rows',
|
|
37
33
|
size: 'constant',
|
|
38
34
|
},
|
|
39
35
|
span: {
|
|
40
36
|
channels: ['row', 'color'],
|
|
41
37
|
value: 'none',
|
|
42
|
-
ramp: 'display',
|
|
43
38
|
hit: 'rows',
|
|
44
39
|
},
|
|
45
40
|
text: {
|
|
46
41
|
channels: ['y', 'row', 'color', 'text'],
|
|
47
42
|
value: 'optional',
|
|
48
|
-
ramp: 'worker',
|
|
49
43
|
hit: false,
|
|
50
44
|
},
|
|
51
45
|
link: {
|
|
52
46
|
channels: ['y', 'row', 'color', 'size'],
|
|
53
47
|
value: 'optional',
|
|
54
|
-
ramp: 'display',
|
|
55
48
|
hit: 'index',
|
|
56
49
|
farFoot: true,
|
|
57
50
|
size: 'channel',
|
|
@@ -72,23 +65,18 @@ export function plotsValue(type) {
|
|
|
72
65
|
export function hitsByIndex(type) {
|
|
73
66
|
return specOf(type).hit === 'index';
|
|
74
67
|
}
|
|
75
|
-
/** Whether a mark type's ramp colour resolves per region, in the worker. */
|
|
76
|
-
export function rampResolvesPerRegion(type) {
|
|
77
|
-
return specOf(type).ramp === 'worker';
|
|
78
|
-
}
|
|
79
68
|
/**
|
|
80
|
-
* The lanes a mark asks the worker to fill: its type's channels,
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* `color` and `colorValue` the colour declaration calls for.
|
|
69
|
+
* The lanes a mark asks the worker to fill: its type's channels, the colour
|
|
70
|
+
* lanes `colorLanesOf` chose for its declaration, and the hit index where the
|
|
71
|
+
* mark answers a hover through one.
|
|
84
72
|
*/
|
|
85
|
-
export function markLanes(type) {
|
|
73
|
+
export function markLanes(type, colorLanes) {
|
|
86
74
|
const spec = specOf(type);
|
|
87
75
|
return [
|
|
88
76
|
...spec.channels.flatMap((channel) => channel === 'shape'
|
|
89
77
|
? ['glyph']
|
|
90
|
-
: channel === 'color'
|
|
91
|
-
?
|
|
78
|
+
: channel === 'color'
|
|
79
|
+
? colorLanes
|
|
92
80
|
: [channel]),
|
|
93
81
|
...(spec.farFoot ? ['x2Ref'] : []),
|
|
94
82
|
...(hitsByIndex(type) ? ['index'] : []),
|
|
@@ -26,6 +26,8 @@ export const DEFAULT_LINK_SHAPE = 'dome';
|
|
|
26
26
|
export const DEFAULT_LINK_STROKE_PX = 2;
|
|
27
27
|
/** The fraction of its row a span fills where its `rowProportion` is left unwritten. */
|
|
28
28
|
export const DEFAULT_ROW_PROPORTION = 1;
|
|
29
|
+
/** The cuts a threshold colour paints, render-core's `MAX_COLOR_CUTS`. */
|
|
30
|
+
export const MAX_THRESHOLD_CUTS = 8;
|
|
29
31
|
export const SIZE_SCALES = ['linear', 'log'];
|
|
30
32
|
export const MARK_SOURCES = ['features', 'density'];
|
|
31
33
|
export const DEFAULT_MARK_SOURCE = 'features';
|
|
@@ -37,7 +39,7 @@ export const BIN_OVERLAP_FIELD = 'overlap';
|
|
|
37
39
|
export const DEFAULT_COVERAGE_AS = 'coverage';
|
|
38
40
|
export const DEFAULT_FLATTEN_FIELD = 'subfeatures';
|
|
39
41
|
export const DEFAULT_CELLS_FIELD = 'seq';
|
|
40
|
-
/** What a `cells` step writes: `state` on every cell, `base` on a mismatch or an insertion, `match` off a gap
|
|
42
|
+
/** What a `cells` step writes: `state` on every cell, `base` on a mismatch or an insertion, `match` off a gap or a reference `N`, `length` on an insertion. */
|
|
41
43
|
export const CELLS_FIELDS = ['state', 'base', 'match', 'length'];
|
|
42
44
|
export const DEFAULT_PILEUP_AS = 'row';
|
|
43
45
|
export const DEFAULT_PILEUP_FIELDS = ['start', 'end'];
|
|
@@ -68,7 +68,7 @@ function describe(schema) {
|
|
|
68
68
|
if (name === 'JexlString') {
|
|
69
69
|
return 'a "jexl:" expression';
|
|
70
70
|
}
|
|
71
|
-
if (name === 'CssColor') {
|
|
71
|
+
if (name === 'CssColor' || name === 'CssColorEntry') {
|
|
72
72
|
return 'a CSS color (a name like "red", "#rrggbb", "rgb()" or "hsl()")';
|
|
73
73
|
}
|
|
74
74
|
if (name) {
|
|
@@ -378,7 +378,7 @@ function explain(data, errors, problems, depth) {
|
|
|
378
378
|
break;
|
|
379
379
|
}
|
|
380
380
|
case 'pattern':
|
|
381
|
-
emit(where, error.schemaPath
|
|
381
|
+
emit(where, /\/CssColor(?:Entry)?\/pattern$/.test(error.schemaPath)
|
|
382
382
|
? `expected ${describe({ $ref: '#/$defs/CssColor' })}, got ${JSON.stringify(error.data)}`
|
|
383
383
|
: `expected a string matching /${String(parent.pattern)}/, got ${JSON.stringify(error.data)}`);
|
|
384
384
|
break;
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
// loading normally with the setting doing nothing.
|
|
9
9
|
//
|
|
10
10
|
// Pure: no filesystem, no process exit. The command wrapper owns both.
|
|
11
|
+
import { fileNameOf, isLooseTrackConfig, matchFormat, } from '@jbrowse/add-track-core';
|
|
11
12
|
import { configManifest } from './configManifest.generated.js';
|
|
12
13
|
import { displayDefaultsForTrackType } from './displayDefaultKeys.js';
|
|
13
14
|
import { isRecord, liftToSnapshot } from './liftConfig.js';
|
|
14
|
-
import { colorProblems, scaleEndProblems } from './markRules/colorScale.js';
|
|
15
|
+
import { colorProblems, colorSlotsOf, scaleEndProblems, scaleEndsOf, } from './markRules/colorScale.js';
|
|
15
16
|
import { markProblems } from './markRules/markProblems.js';
|
|
16
17
|
import { hasDeclaredShape, rootConfigurationSlots, schemaProblems, } from './schemaValidate.js';
|
|
17
18
|
import { didYouMean } from './suggest.js';
|
|
@@ -103,7 +104,7 @@ function checkPluggable(obj, group, groupLabel, where, report) {
|
|
|
103
104
|
}
|
|
104
105
|
function checkTrack(track, index, manifest, report, ctx) {
|
|
105
106
|
const where = `tracks[${index}]`;
|
|
106
|
-
const loose =
|
|
107
|
+
const loose = isLooseTrackConfig(track);
|
|
107
108
|
const trackId = track.trackId;
|
|
108
109
|
if (typeof trackId === 'string' && trackId) {
|
|
109
110
|
if (ctx.seenTrackIds.has(trackId)) {
|
|
@@ -129,6 +130,19 @@ function checkTrack(track, index, manifest, report, ctx) {
|
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
}
|
|
133
|
+
if (loose) {
|
|
134
|
+
const file = fileNameOf(track.uri);
|
|
135
|
+
const spec = matchFormat(file)?.spec;
|
|
136
|
+
if (!spec || !('adapterType' in spec)) {
|
|
137
|
+
const message = `JBrowse infers no track from "${file}", so the track fails to load: give it an "adapter"`;
|
|
138
|
+
if (ctx.declaresPlugins) {
|
|
139
|
+
report.warn(`${where}.uri`, `${message}, unless a plugin it declares guesses this extension`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
report.error(`${where}.uri`, message);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
132
146
|
checkPluggable(track, manifest.tracks, 'track', where, report);
|
|
133
147
|
if (!loose) {
|
|
134
148
|
checkPluggable(track.adapter, manifest.adapters, 'adapter', `${where}.adapter`, report);
|
|
@@ -276,7 +290,7 @@ function declaredEntries(lifted, display, slot, slots) {
|
|
|
276
290
|
return (slots.find(s => s.name === slot)?.defaultEntries ?? []);
|
|
277
291
|
}
|
|
278
292
|
return Array.isArray(list)
|
|
279
|
-
? list.map((entry, i) => hasDeclaredShape(written[i], `${SLOTS_POINTER}/${slot}
|
|
293
|
+
? list.map((entry, i) => hasDeclaredShape([written[i]], `${SLOTS_POINTER}/${slot}`)
|
|
280
294
|
? entry
|
|
281
295
|
: undefined)
|
|
282
296
|
: [];
|
|
@@ -330,18 +344,9 @@ function pushOnce(report, problem) {
|
|
|
330
344
|
report.problems.push(problem);
|
|
331
345
|
}
|
|
332
346
|
}
|
|
333
|
-
function numberOf(value) {
|
|
334
|
-
return typeof value === 'number' ? value : undefined;
|
|
335
|
-
}
|
|
336
|
-
function scaleEndsOf(scale) {
|
|
337
|
-
return {
|
|
338
|
-
domainMin: numberOf(scale.domainMin),
|
|
339
|
-
domainMax: numberOf(scale.domainMax),
|
|
340
|
-
domainQuantile: numberOf(scale.domainQuantile),
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
347
|
function scalesOf(scales) {
|
|
344
|
-
|
|
348
|
+
const { y } = scales;
|
|
349
|
+
return isRecord(y) ? { y: scaleEndsOf(name => y[name]) } : {};
|
|
345
350
|
}
|
|
346
351
|
function declaredMembers(written, slot) {
|
|
347
352
|
const declared = new Set((slot.subSlots ?? []).map(s => s.name));
|
|
@@ -371,19 +376,15 @@ function checkScaleSlots(display, slots, where, report) {
|
|
|
371
376
|
}
|
|
372
377
|
const members = declaredMembers(written, slot);
|
|
373
378
|
if (slot.fieldPresets) {
|
|
374
|
-
for (const problem of colorProblems(
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
scale: typeof members.scale === 'string' ? members.scale : undefined,
|
|
378
|
-
domain: Array.isArray(members.domain) ? members.domain : undefined,
|
|
379
|
-
range: Array.isArray(members.range) ? members.range : undefined,
|
|
380
|
-
labels: Array.isArray(members.labels) ? members.labels : undefined,
|
|
381
|
-
}, slot.fieldPresets)) {
|
|
379
|
+
for (const problem of colorProblems(colorSlotsOf(name => name === 'field'
|
|
380
|
+
? (members.field ?? slot.fieldDefault)
|
|
381
|
+
: members[name]), slot.fieldPresets)) {
|
|
382
382
|
warn(`${where}.${slot.name}`, problem);
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
|
-
|
|
386
|
-
|
|
385
|
+
const { y } = members;
|
|
386
|
+
if (slot.name === 'scales' && isRecord(y)) {
|
|
387
|
+
for (const problem of scaleEndProblems(scaleEndsOf(name => y[name]))) {
|
|
387
388
|
warn(`${where}.scales.y`, problem);
|
|
388
389
|
}
|
|
389
390
|
}
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '5.0.0-beta.
|
|
1
|
+
export const version = '5.0.0-beta.13';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/cli",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A command line tool for working with JBrowse 2",
|
|
6
6
|
"keywords": [
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"cli-progress": "^3.12.0",
|
|
32
32
|
"serve-handler": "^6.1.7",
|
|
33
33
|
"tmp": "^0.2.7",
|
|
34
|
-
"@jbrowse/
|
|
35
|
-
"@jbrowse/
|
|
36
|
-
"@jbrowse/text-indexing-core": "^5.0.0-beta.
|
|
34
|
+
"@jbrowse/cigar-utils": "^5.0.0-beta.13",
|
|
35
|
+
"@jbrowse/add-track-core": "^5.0.0-beta.13",
|
|
36
|
+
"@jbrowse/text-indexing-core": "^5.0.0-beta.13"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|