@jbrowse/cli 5.0.0-beta.13 → 5.0.0-beta.14
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 +19 -38
- package/dist/commands/add-track-utils/adapter-utils.js +0 -45
- package/dist/commands/add-track.js +5 -28
- package/dist/commands/make-density/density-generator.js +1 -1
- package/dist/commands/make-density/index.js +14 -26
- package/dist/commands/make-pif/index.js +1 -1
- package/dist/commands/text-index/file-list.js +1 -1
- package/dist/commands/validate/configManifest.generated.js +1027 -273
- package/dist/commands/validate/configSchema.generated.js +3227 -1445
- package/dist/commands/validate/index.js +4 -3
- package/dist/commands/validate/markRules/colorScale.js +124 -26
- package/dist/commands/validate/markRules/markProblems.js +49 -99
- package/dist/commands/validate/markRules/markRuleFacts.js +1 -1
- package/dist/commands/validate/markRules/markSpecs.js +4 -2
- package/dist/commands/validate/markRules/markVocabulary.js +21 -24
- package/dist/commands/validate/markRules/stepChannels.js +13 -47
- package/dist/commands/validate/markRules/stepOutputs.js +202 -0
- package/dist/commands/validate/schemaValidate.js +5 -2
- package/dist/commands/validate/validateConfig.js +47 -13
- package/dist/index.js +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/dist/commands/shared/density.js +0 -12
- package/dist/commands/validate/markRules/aggregateFieldName.js +0 -9
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// Generated by scripts/generateMarkRules.ts from plugins/marks/src/LinearMarkDisplay/markVocabulary.ts: do not edit, `pnpm autogen` rewrites it.
|
|
2
2
|
/**
|
|
3
3
|
* The names a `marks` list is written in and the ones a slot left off reads
|
|
4
|
-
* as, in a module that imports
|
|
4
|
+
* as, in a module that imports only the rule list's seam: the config schema declares its
|
|
5
5
|
* enumerations and defaults from here, the rule list reads the same constants,
|
|
6
|
-
* and `jbrowse validate` carries a copy of both.
|
|
6
|
+
* and `jbrowse validate` carries a copy of both. What a step writes, and
|
|
7
|
+
* the defaults it writes under, are the step table's.
|
|
7
8
|
*/
|
|
9
|
+
export { BIN_OVERLAP_FIELD, DEFAULT_AGGREGATE_OP, DEFAULT_BIN_AS, DEFAULT_COVERAGE_AS, DEFAULT_FORMULA_AS, DEFAULT_PILEUP_AS, } from './markRuleFacts.js';
|
|
8
10
|
export const MARK_TYPES = [
|
|
9
11
|
'bar',
|
|
10
12
|
'point',
|
|
@@ -26,34 +28,29 @@ export const DEFAULT_LINK_SHAPE = 'dome';
|
|
|
26
28
|
export const DEFAULT_LINK_STROKE_PX = 2;
|
|
27
29
|
/** The fraction of its row a span fills where its `rowProportion` is left unwritten. */
|
|
28
30
|
export const DEFAULT_ROW_PROPORTION = 1;
|
|
29
|
-
/** The cuts a threshold
|
|
31
|
+
/** The cuts a threshold color paints, render-core's `MAX_COLOR_CUTS`. */
|
|
30
32
|
export const MAX_THRESHOLD_CUTS = 8;
|
|
31
33
|
export const SIZE_SCALES = ['linear', 'log'];
|
|
32
|
-
export const MARK_SOURCES = ['features', 'density'];
|
|
33
|
-
export const DEFAULT_MARK_SOURCE = 'features';
|
|
34
|
-
export const DEFAULT_FORMULA_AS = 'value';
|
|
35
34
|
export const DEFAULT_BIN_FIELD = 'start';
|
|
36
|
-
export const DEFAULT_BIN_AS = ['start', 'end'];
|
|
37
|
-
/** What a `bin` over `fields` writes on each piece: the bases of its interval inside its bin. */
|
|
38
|
-
export const BIN_OVERLAP_FIELD = 'overlap';
|
|
39
|
-
export const DEFAULT_COVERAGE_AS = 'coverage';
|
|
40
35
|
export const DEFAULT_FLATTEN_FIELD = 'subfeatures';
|
|
41
36
|
export const DEFAULT_CELLS_FIELD = 'seq';
|
|
42
|
-
/**
|
|
43
|
-
export const
|
|
44
|
-
|
|
37
|
+
/** The rows before and after each row a `window` reads: a moving average over 21. */
|
|
38
|
+
export const DEFAULT_WINDOW_FRAME = [-10, 10];
|
|
39
|
+
/**
|
|
40
|
+
* Whether a `window`'s `frame` is what the step reads: two whole row offsets,
|
|
41
|
+
* either `null` for unbounded, the first not past the second.
|
|
42
|
+
*/
|
|
43
|
+
export function isWindowFrame(frame) {
|
|
44
|
+
if (!Array.isArray(frame) || frame.length !== 2) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const [before, after] = frame;
|
|
48
|
+
const end = (v) => v === null || Number.isInteger(v);
|
|
49
|
+
return (end(before) &&
|
|
50
|
+
end(after) &&
|
|
51
|
+
(before === null || after === null || Number(before) <= Number(after)));
|
|
52
|
+
}
|
|
45
53
|
export const DEFAULT_PILEUP_FIELDS = ['start', 'end'];
|
|
46
54
|
export const DEFAULT_TEXT_FIELD = 'name';
|
|
47
55
|
export const DEFAULT_X2 = 'end';
|
|
48
|
-
/** What a `mate` step writes on every feature it answers. */
|
|
49
|
-
export const MATE_FIELDS = [
|
|
50
|
-
'mate.refName',
|
|
51
|
-
'mate.start',
|
|
52
|
-
'mate.end',
|
|
53
|
-
'mate.mateDirection',
|
|
54
|
-
'mateDirection',
|
|
55
|
-
'alt',
|
|
56
|
-
'svType',
|
|
57
|
-
];
|
|
58
56
|
export const AGGREGATE_OPS = ['count', 'sum', 'mean', 'min', 'max'];
|
|
59
|
-
export const DEFAULT_AGGREGATE_OP = 'count';
|
|
@@ -1,55 +1,21 @@
|
|
|
1
1
|
// Generated by scripts/generateMarkRules.ts from plugins/marks/src/LinearMarkDisplay/stepChannels.ts: do not edit, `pnpm autogen` rewrites it.
|
|
2
|
-
import {
|
|
3
|
-
import { DEFAULT_AGGREGATE_OP, DEFAULT_COVERAGE_AS, DEFAULT_PILEUP_AS, } from './markVocabulary.js';
|
|
2
|
+
import { stepsOutput } from './markRuleFacts.js';
|
|
4
3
|
/** The far end a `mate` step writes, as `x2` reads a position on another sequence. */
|
|
5
4
|
export const MATE_X2 = { chrom: 'mate.refName', pos: 'mate.start' };
|
|
6
|
-
function outputOf(step, fallback) {
|
|
7
|
-
return typeof step.as === 'string' && step.as !== '' ? step.as : fallback;
|
|
8
|
-
}
|
|
9
5
|
/**
|
|
10
6
|
* What {@link StepChannels} a mark's steps leave it, read over the display's,
|
|
11
|
-
* the facet's and then the mark's own steps in the order they run.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* summaries names no `y`.
|
|
7
|
+
* the facet's and then the mark's own steps in the order they run. A step
|
|
8
|
+
* that makes its features anew keeps a channel only where it keeps the field
|
|
9
|
+
* that channel read, as an `aggregate` grouped by a pileup's `row` does.
|
|
15
10
|
*/
|
|
16
11
|
export function stepChannels(steps) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
case 'aggregate': {
|
|
28
|
-
const [only, ...rest] = step.ops ?? [];
|
|
29
|
-
channels =
|
|
30
|
-
only && rest.length === 0
|
|
31
|
-
? {
|
|
32
|
-
y: aggregateFieldName({
|
|
33
|
-
op: only.op ?? DEFAULT_AGGREGATE_OP,
|
|
34
|
-
field: only.field,
|
|
35
|
-
as: only.as,
|
|
36
|
-
}),
|
|
37
|
-
}
|
|
38
|
-
: {};
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
case 'pileup': {
|
|
42
|
-
channels = { ...channels, row: outputOf(step, DEFAULT_PILEUP_AS) };
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
case 'mate': {
|
|
46
|
-
channels = { ...channels, x2: MATE_X2 };
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
default: {
|
|
50
|
-
break;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return channels;
|
|
12
|
+
const { channels, surviving } = stepsOutput(steps);
|
|
13
|
+
const y = channels.get('y');
|
|
14
|
+
const row = channels.get('row');
|
|
15
|
+
const x2 = channels.has('x2') && (!surviving || surviving.has(MATE_X2.pos));
|
|
16
|
+
return {
|
|
17
|
+
...(y === undefined ? {} : { y }),
|
|
18
|
+
...(row === undefined ? {} : { row }),
|
|
19
|
+
...(x2 ? { x2: MATE_X2 } : {}),
|
|
20
|
+
};
|
|
55
21
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Generated by scripts/generateMarkRules.ts from packages/core/src/util/stepOutputs.ts: do not edit, `pnpm autogen` rewrites it.
|
|
2
|
+
/**
|
|
3
|
+
* What each transform step does to the rows it reads, stated once for the
|
|
4
|
+
* runtime's facet split, the validator, the field pickers and the channels a
|
|
5
|
+
* mark adopts by default. Imports nothing, so the validator's copy of the mark
|
|
6
|
+
* rule list carries it unchanged.
|
|
7
|
+
*
|
|
8
|
+
* Every step type answers three things:
|
|
9
|
+
* - `rowwise`: it answers each row from that row alone and in order, so a
|
|
10
|
+
* facet split may run ahead of it.
|
|
11
|
+
* - `replacesRows`: it makes its rows anew (an aggregate, a coverage), so a
|
|
12
|
+
* field survives it only where it writes that field or keeps it as a group.
|
|
13
|
+
* - `writes`: the fields it sets, each with the kind of value it holds and the
|
|
14
|
+
* channel a mark leaving that channel unwritten reads it through.
|
|
15
|
+
*/
|
|
16
|
+
/** The field an aggregate op writes: `as` where named, else `count` for a count, else `<op>_<field>`. */
|
|
17
|
+
export function aggregateFieldName({ op, field, as, }) {
|
|
18
|
+
return as || (op === 'count' || !field ? op : `${op}_${field}`);
|
|
19
|
+
}
|
|
20
|
+
export const DEFAULT_FORMULA_AS = 'value';
|
|
21
|
+
export const DEFAULT_COVERAGE_AS = 'coverage';
|
|
22
|
+
export const DEFAULT_PILEUP_AS = 'row';
|
|
23
|
+
export const DEFAULT_AGGREGATE_OP = 'count';
|
|
24
|
+
export const DEFAULT_BIN_AS = ['start', 'end'];
|
|
25
|
+
/** What a `bin` over `fields` writes on each piece: the bases of its interval inside its bin. */
|
|
26
|
+
export const BIN_OVERLAP_FIELD = 'overlap';
|
|
27
|
+
/** 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. */
|
|
28
|
+
export const CELLS_FIELDS = ['state', 'base', 'match', 'length'];
|
|
29
|
+
/** What a `mate` step writes on every feature it answers. */
|
|
30
|
+
export const MATE_FIELDS = [
|
|
31
|
+
'mate.refName',
|
|
32
|
+
'mate.start',
|
|
33
|
+
'mate.end',
|
|
34
|
+
'mate.mateDirection',
|
|
35
|
+
'mateDirection',
|
|
36
|
+
'alt',
|
|
37
|
+
'svType',
|
|
38
|
+
];
|
|
39
|
+
/** Where a row stands, which every row keeps through every step. */
|
|
40
|
+
export const POSITION_FIELDS = ['refName', 'start', 'end'];
|
|
41
|
+
function named(as, fallback) {
|
|
42
|
+
return typeof as === 'string' && as !== '' ? as : fallback;
|
|
43
|
+
}
|
|
44
|
+
function binEdges(step) {
|
|
45
|
+
const { as } = step;
|
|
46
|
+
return Array.isArray(as) && as.length === 2 ? as : DEFAULT_BIN_AS;
|
|
47
|
+
}
|
|
48
|
+
// A single op names the `y` its mark reads; two leave the mark to choose.
|
|
49
|
+
function summaries(step) {
|
|
50
|
+
const ops = step.ops ?? [];
|
|
51
|
+
return ops.map(({ op = DEFAULT_AGGREGATE_OP, field, as }) => ({
|
|
52
|
+
field: aggregateFieldName({ op, field, as }),
|
|
53
|
+
kind: 'numeric',
|
|
54
|
+
...(ops.length === 1 ? { fills: 'y' } : {}),
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
const kept = (field) => ({ field, kind: 'unknown' });
|
|
58
|
+
export const STEP_OUTPUTS = {
|
|
59
|
+
filter: { rowwise: true, replacesRows: false, writes: () => [] },
|
|
60
|
+
formula: {
|
|
61
|
+
rowwise: true,
|
|
62
|
+
replacesRows: false,
|
|
63
|
+
writes: step => [
|
|
64
|
+
{ field: named(step.as, DEFAULT_FORMULA_AS), kind: 'numeric' },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
bin: {
|
|
68
|
+
rowwise: true,
|
|
69
|
+
replacesRows: false,
|
|
70
|
+
writes: step => [
|
|
71
|
+
...binEdges(step).map(field => ({ field, kind: 'numeric' })),
|
|
72
|
+
...(step.fields?.length === 2
|
|
73
|
+
? [{ field: BIN_OVERLAP_FIELD, kind: 'numeric' }]
|
|
74
|
+
: []),
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
cells: {
|
|
78
|
+
rowwise: true,
|
|
79
|
+
replacesRows: false,
|
|
80
|
+
writes: () => [
|
|
81
|
+
{ field: 'start', kind: 'numeric' },
|
|
82
|
+
{ field: 'end', kind: 'numeric' },
|
|
83
|
+
{ field: 'state', kind: 'categorical' },
|
|
84
|
+
{ field: 'base', kind: 'categorical' },
|
|
85
|
+
{ field: 'match', kind: 'numeric' },
|
|
86
|
+
{ field: 'length', kind: 'numeric' },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
aggregate: {
|
|
90
|
+
rowwise: false,
|
|
91
|
+
replacesRows: true,
|
|
92
|
+
writes: (step, edges) => [
|
|
93
|
+
...(step.groupby?.length ? step.groupby : edges).map(kept),
|
|
94
|
+
...summaries(step),
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
window: { rowwise: false, replacesRows: false, writes: summaries },
|
|
98
|
+
coverage: {
|
|
99
|
+
rowwise: false,
|
|
100
|
+
replacesRows: true,
|
|
101
|
+
writes: step => [
|
|
102
|
+
{
|
|
103
|
+
field: named(step.as, DEFAULT_COVERAGE_AS),
|
|
104
|
+
kind: 'numeric',
|
|
105
|
+
fills: 'y',
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
pileup: {
|
|
110
|
+
rowwise: false,
|
|
111
|
+
replacesRows: false,
|
|
112
|
+
writes: step => [
|
|
113
|
+
{
|
|
114
|
+
field: named(step.as, DEFAULT_PILEUP_AS),
|
|
115
|
+
kind: 'numeric',
|
|
116
|
+
fills: 'row',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
flatten: {
|
|
121
|
+
rowwise: false,
|
|
122
|
+
replacesRows: false,
|
|
123
|
+
writes: step => [
|
|
124
|
+
...(step.index ? [{ field: step.index, kind: 'numeric' }] : []),
|
|
125
|
+
...(step.key ? [{ field: step.key, kind: 'categorical' }] : []),
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
mate: {
|
|
129
|
+
rowwise: false,
|
|
130
|
+
replacesRows: false,
|
|
131
|
+
writes: () => MATE_FIELDS.map(field => ({
|
|
132
|
+
field,
|
|
133
|
+
kind: field === 'mate.start' || field === 'mate.end'
|
|
134
|
+
? 'numeric'
|
|
135
|
+
: 'categorical',
|
|
136
|
+
...(field === 'mate.refName' ? { fills: 'x2' } : {}),
|
|
137
|
+
})),
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
function outputOf(step) {
|
|
141
|
+
return Object.hasOwn(STEP_OUTPUTS, step.type)
|
|
142
|
+
? STEP_OUTPUTS[step.type]
|
|
143
|
+
: undefined;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The fields one step writes. A step this table does not know writes nothing
|
|
147
|
+
* it can name.
|
|
148
|
+
*/
|
|
149
|
+
export function stepWrites(step, edges = DEFAULT_BIN_AS) {
|
|
150
|
+
return outputOf(step)?.writes(step, edges) ?? [];
|
|
151
|
+
}
|
|
152
|
+
/** Whether a step answers each row from that row alone, in order. */
|
|
153
|
+
export function isRowwise(step) {
|
|
154
|
+
return outputOf(step)?.rowwise ?? false;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* What a run of steps leaves: every field written, the last write of each
|
|
158
|
+
* winning, and, where a step made the rows anew, the only fields that survive
|
|
159
|
+
* it (`undefined` while the input's own fields all do). `channels` are what
|
|
160
|
+
* the output fills where a mark leaves them unwritten.
|
|
161
|
+
*/
|
|
162
|
+
export function stepsOutput(steps) {
|
|
163
|
+
let edges = DEFAULT_BIN_AS;
|
|
164
|
+
let surviving;
|
|
165
|
+
const written = new Map();
|
|
166
|
+
let channels = new Map();
|
|
167
|
+
for (const step of steps) {
|
|
168
|
+
const output = step && outputOf(step);
|
|
169
|
+
if (!step || !output) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const writes = output.writes(step, edges);
|
|
173
|
+
if (output.replacesRows) {
|
|
174
|
+
surviving = new Set(POSITION_FIELDS);
|
|
175
|
+
for (const [field] of written) {
|
|
176
|
+
if (!writes.some(w => w.field === field)) {
|
|
177
|
+
written.delete(field);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (const w of writes) {
|
|
181
|
+
surviving.add(w.field);
|
|
182
|
+
}
|
|
183
|
+
channels = new Map([...channels].filter(([, f]) => surviving.has(f)));
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
for (const w of writes) {
|
|
187
|
+
surviving?.add(w.field);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
for (const w of writes) {
|
|
191
|
+
const held = written.get(w.field);
|
|
192
|
+
written.set(w.field, w.kind === 'unknown' && held ? { ...held, field: w.field } : w);
|
|
193
|
+
if (w.fills) {
|
|
194
|
+
channels.set(w.fills, w.field);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (step.type === 'bin') {
|
|
198
|
+
edges = binEdges(step);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return { written: [...written.values()], surviving, channels };
|
|
202
|
+
}
|
|
@@ -175,8 +175,8 @@ function unknownKeyMessage(key, schema, accepted) {
|
|
|
175
175
|
if (title.endsWith('TrackEntry')) {
|
|
176
176
|
return `"${key}" is neither a config slot nor a property of ${title.replace(/TrackEntry$/, '')}${guess} — the entry's keys are folded onto that display, and one it does not declare silently does nothing`;
|
|
177
177
|
}
|
|
178
|
-
if (schema['x-closed']
|
|
179
|
-
return `unknown slot "${key}"${guess} — ${title} takes ${listed(accepted)}
|
|
178
|
+
if (schema['x-closed']) {
|
|
179
|
+
return `unknown slot "${key}"${guess} — ${title} takes ${listed(accepted)}; JBrowse warns on the console and loads without it, so this setting does nothing`;
|
|
180
180
|
}
|
|
181
181
|
return `unknown slot "${key}"${guess} — JBrowse ignores keys it does not declare, so this setting silently does nothing`;
|
|
182
182
|
}
|
|
@@ -422,4 +422,7 @@ export function hasDeclaredShape(data, pointer) {
|
|
|
422
422
|
* beside them, the way `LinearGenomeViewPlugin` is core's.
|
|
423
423
|
*/
|
|
424
424
|
export const rootConfigurationSlots = new Set(Object.keys(defs.RootConfiguration.properties));
|
|
425
|
+
export function hasSchemaDefinition(name) {
|
|
426
|
+
return Object.hasOwn(defs, name);
|
|
427
|
+
}
|
|
425
428
|
export { schemaId as configSchemaUrl };
|
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
// cross-references a schema cannot express and the warnings for stale
|
|
4
4
|
// spellings a migration still rewrites.
|
|
5
5
|
//
|
|
6
|
-
// Why a validator at all, given MST type-checks a config on load:
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// Why a validator at all, given MST type-checks a config on load: a key no
|
|
7
|
+
// schema declares does nothing, named on the console by a display and dropped
|
|
8
|
+
// in silence by a track, an adapter or a session snapshot, so a misspelt slot
|
|
9
|
+
// leaves the track loading normally.
|
|
9
10
|
//
|
|
10
11
|
// Pure: no filesystem, no process exit. The command wrapper owns both.
|
|
11
|
-
import { fileNameOf, isLooseTrackConfig, matchFormat, } from '@jbrowse/add-track-core';
|
|
12
|
+
import { fileNameOf, isLooseTrackConfig, matchFormat, trackTypeForAdapter, } from '@jbrowse/add-track-core';
|
|
12
13
|
import { configManifest } from './configManifest.generated.js';
|
|
13
14
|
import { displayDefaultsForTrackType } from './displayDefaultKeys.js';
|
|
14
15
|
import { isRecord, liftToSnapshot } from './liftConfig.js';
|
|
15
|
-
import { colorProblems, colorSlotsOf, scaleEndProblems, scaleEndsOf, } from './markRules/colorScale.js';
|
|
16
|
+
import { colorProblems, colorSlotsOf, scaleEndProblems, scaleEndsOf, withImpliedField, } from './markRules/colorScale.js';
|
|
16
17
|
import { markProblems } from './markRules/markProblems.js';
|
|
17
18
|
import { hasDeclaredShape, rootConfigurationSlots, schemaProblems, } from './schemaValidate.js';
|
|
18
19
|
import { didYouMean } from './suggest.js';
|
|
@@ -44,6 +45,12 @@ class Report {
|
|
|
44
45
|
const SELF_SUPPLIED = {
|
|
45
46
|
sequenceAdapter: '`sequenceAdapter` is set by hand. JBrowse takes the sequence from the assembly the track is displayed against, so this is only needed to read some OTHER sequence — and it pins the track to that source even when the assembly changes. Delete it unless that is what you meant',
|
|
46
47
|
};
|
|
48
|
+
// What replaced a dropped legacy key, where there is something to say: a
|
|
49
|
+
// config written before v4 carries a `sequenceAdapter` on its BAM and CRAM
|
|
50
|
+
// adapters.
|
|
51
|
+
const DROPPED = {
|
|
52
|
+
sequenceAdapter: 'JBrowse takes the sequence from the assembly the track is displayed against. ',
|
|
53
|
+
};
|
|
47
54
|
// The warnings a slot table carries: a legacy key or value a migration
|
|
48
55
|
// rewrites, and a declared key JBrowse fills in for itself.
|
|
49
56
|
function warnSlots(obj, entry, where, report) {
|
|
@@ -56,7 +63,9 @@ function warnSlots(obj, entry, where, report) {
|
|
|
56
63
|
report.warn(`${where}.${key}`, `${JSON.stringify(value)} is a legacy value of "${key}" that a migration rewrites — the config loads, but the current spelling is one of the enum's members`);
|
|
57
64
|
}
|
|
58
65
|
else if (!declared.has(key) && entry.legacyKeys?.includes(key)) {
|
|
59
|
-
report.warn(`${where}.${key}`,
|
|
66
|
+
report.warn(`${where}.${key}`, entry.droppedKeys?.includes(key)
|
|
67
|
+
? `"${key}" is a legacy key that is never read. ${DROPPED[key] ?? ''}The config loads, and the key can be deleted`
|
|
68
|
+
: `"${key}" is a legacy key that a migration rewrites into current slots — the config loads, but check the settings inside it landed`);
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
for (const slot of entry.slots) {
|
|
@@ -337,7 +346,7 @@ function checkMarkDisplay(display, manifest, where, report) {
|
|
|
337
346
|
});
|
|
338
347
|
}
|
|
339
348
|
}
|
|
340
|
-
// A problem the
|
|
349
|
+
// A problem the color and scale rules and the mark rule list both reach, such
|
|
341
350
|
// as scales.y's ends on a mark display, is reported once.
|
|
342
351
|
function pushOnce(report, problem) {
|
|
343
352
|
if (!report.problems.some(p => p.where === problem.where && p.rule === problem.rule)) {
|
|
@@ -352,7 +361,7 @@ function declaredMembers(written, slot) {
|
|
|
352
361
|
const declared = new Set((slot.subSlots ?? []).map(s => s.name));
|
|
353
362
|
return Object.fromEntries(Object.entries(written).filter(([key]) => declared.has(key)));
|
|
354
363
|
}
|
|
355
|
-
// What the display's
|
|
364
|
+
// What the display's color objects and its value scale say together that it
|
|
356
365
|
// cannot draw as written: its schema's fieldPresets stand in for the plugin
|
|
357
366
|
// code the CLI does not run, and every quantitative scale's ends answer to
|
|
358
367
|
// one rule.
|
|
@@ -376,9 +385,9 @@ function checkScaleSlots(display, slots, where, report) {
|
|
|
376
385
|
}
|
|
377
386
|
const members = declaredMembers(written, slot);
|
|
378
387
|
if (slot.fieldPresets) {
|
|
379
|
-
for (const problem of colorProblems(colorSlotsOf(name => name === 'field'
|
|
388
|
+
for (const problem of colorProblems(withImpliedField(colorSlotsOf(name => name === 'field'
|
|
380
389
|
? (members.field ?? slot.fieldDefault)
|
|
381
|
-
: members[name]), slot.fieldPresets)) {
|
|
390
|
+
: members[name]), slot.impliedField), slot.fieldPresets)) {
|
|
382
391
|
warn(`${where}.${slot.name}`, problem);
|
|
383
392
|
}
|
|
384
393
|
}
|
|
@@ -395,6 +404,30 @@ function checkScaleSlots(display, slots, where, report) {
|
|
|
395
404
|
// entry. Only a mark display declares `marks`, so the walk is over the whole
|
|
396
405
|
// file, past a place where the schema refused the key: a session display node
|
|
397
406
|
// drops it.
|
|
407
|
+
// a track's type as written, or as a loose `{ trackId, uri }` entry's file
|
|
408
|
+
// name decides it, the way add-track does
|
|
409
|
+
function trackTypeOf(node) {
|
|
410
|
+
if (typeof node.type === 'string') {
|
|
411
|
+
return node.type;
|
|
412
|
+
}
|
|
413
|
+
if (typeof node.uri !== 'string') {
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
const fileName = fileNameOf(node.uri);
|
|
417
|
+
const format = matchFormat(fileName, typeof node.adapterType === 'string' ? node.adapterType : undefined);
|
|
418
|
+
return (format?.trackType ??
|
|
419
|
+
(format && 'adapterType' in format.spec
|
|
420
|
+
? trackTypeForAdapter(format.spec.adapterType, fileName)
|
|
421
|
+
: undefined));
|
|
422
|
+
}
|
|
423
|
+
// The shorthand router hands a `displayDefaults` key only to the displays
|
|
424
|
+
// whose slot takes its value (`collectDisplayOverrides`), so a categorical
|
|
425
|
+
// color on a FeatureTrack never reaches its quantitative display's `score`
|
|
426
|
+
// presets. The same cut here, through each display's own schema branch.
|
|
427
|
+
function displayDefaultsReaching(display, defaults) {
|
|
428
|
+
return Object.fromEntries(Object.entries(defaults).filter(([key, value]) => schemaProblems({ type: display, [key]: value }, `/$defs/${display}`)
|
|
429
|
+
.length === 0));
|
|
430
|
+
}
|
|
398
431
|
function checkMarkDisplays(node, manifest, where, report) {
|
|
399
432
|
if (Array.isArray(node)) {
|
|
400
433
|
for (const [i, item] of node.entries()) {
|
|
@@ -408,10 +441,11 @@ function checkMarkDisplays(node, manifest, where, report) {
|
|
|
408
441
|
if (slots) {
|
|
409
442
|
checkScaleSlots(node, slots, where, report);
|
|
410
443
|
}
|
|
411
|
-
|
|
412
|
-
|
|
444
|
+
const trackType = trackTypeOf(node);
|
|
445
|
+
if (trackType && isRecord(node.displayDefaults)) {
|
|
446
|
+
for (const display of displayDefaultsForTrackType(trackType, manifest)
|
|
413
447
|
.displayTypes) {
|
|
414
|
-
checkScaleSlots(node.displayDefaults, manifest.displays[display]?.slots ?? [], `${where}.displayDefaults`, report);
|
|
448
|
+
checkScaleSlots(displayDefaultsReaching(display, node.displayDefaults), manifest.displays[display]?.slots ?? [], `${where}.displayDefaults`, report);
|
|
415
449
|
}
|
|
416
450
|
}
|
|
417
451
|
if (holdsMarkRules(node, manifest) &&
|
package/dist/index.js
CHANGED
|
@@ -51,7 +51,7 @@ const registry = [
|
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
name: 'make-density',
|
|
54
|
-
summary: 'Counts feature starts per bin into a bigWig
|
|
54
|
+
summary: 'Counts feature starts per bin into a bigWig, with bedGraphToBigWig',
|
|
55
55
|
load: () => import('./commands/make-density/index.js').then(m => m.run),
|
|
56
56
|
},
|
|
57
57
|
{
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '5.0.0-beta.
|
|
1
|
+
export const version = '5.0.0-beta.14';
|
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.14",
|
|
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/add-track-core": "^5.0.0-beta.14",
|
|
35
|
+
"@jbrowse/cigar-utils": "^5.0.0-beta.14",
|
|
36
|
+
"@jbrowse/text-indexing-core": "^5.0.0-beta.14"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Where the density sidecar for a data file lives: `<file>.density.bw`, with a
|
|
3
|
-
* `.gz`/`.bgz` stripped first so `genes.gff3.gz` yields `genes.gff3.density.bw`
|
|
4
|
-
* rather than a name carrying the compression of a file it is not compressed
|
|
5
|
-
* like.
|
|
6
|
-
*
|
|
7
|
-
* One rule, two commands: `make-density` writes here by default and `add-track`
|
|
8
|
-
* probes here, so a sidecar built with no `--out` is the one the track picks up.
|
|
9
|
-
*/
|
|
10
|
-
export function densitySidecarPath(file) {
|
|
11
|
-
return `${file.replace(/\.b?gz$/i, '')}.density.bw`;
|
|
12
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
// Generated by scripts/generateMarkRules.ts from packages/core/src/util/aggregateFieldName.ts: do not edit, `pnpm autogen` rewrites it.
|
|
2
|
-
/**
|
|
3
|
-
* The field an aggregate op writes: `as` where named, else `count` for a
|
|
4
|
-
* count, else `<op>_<field>`. Imports nothing, so the validator's copy of the
|
|
5
|
-
* mark rule list carries it unchanged.
|
|
6
|
-
*/
|
|
7
|
-
export function aggregateFieldName({ op, field, as, }) {
|
|
8
|
-
return as || (op === 'count' || !field ? op : `${op}_${field}`);
|
|
9
|
-
}
|