1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
// Thing: base class for wiki data types, providing interfaces generally useful
// to all wiki data objects on top of foundational CacheableObject behavior.
import {inspect} from 'node:util';
import CacheableObject from '#cacheable-object';
import {colors} from '#cli';
export default class Thing extends CacheableObject {
static referenceType = Symbol.for('Thing.referenceType');
static friendlyName = Symbol.for('Thing.friendlyName');
static getPropertyDescriptors = Symbol.for('Thing.getPropertyDescriptors');
static getSerializeDescriptors = Symbol.for('Thing.getSerializeDescriptors');
static findSpecs = Symbol.for('Thing.findSpecs');
static findThisThingOnly = Symbol.for('Thing.findThisThingOnly');
static yamlDocumentSpec = Symbol.for('Thing.yamlDocumentSpec');
static getYamlLoadingSpec = Symbol.for('Thing.getYamlLoadingSpec');
static isThingConstructor = Symbol.for('Thing.isThingConstructor');
static isThing = Symbol.for('Thing.isThing');
// To detect:
// Symbol.for('Thing.isThingConstructor') in constructor
static [Symbol.for('Thing.isThingConstructor')] = NaN;
static [CacheableObject.propertyDescriptors] = {
// To detect:
// Object.hasOwn(object, Symbol.for('Thing.isThing'))
[Symbol.for('Thing.isThing')]: {
flags: {expose: true},
expose: {compute: () => NaN},
},
};
static [Symbol.for('Thing.selectAll')] = _wikiData => [];
// Default custom inspect function, which may be overridden by Thing
// subclasses. This will be used when displaying aggregate errors and other
// command-line logging - it's the place to provide information useful in
// identifying the Thing being presented.
[inspect.custom]() {
const constructorName = this.constructor.name;
let name;
try {
if (this.name) {
name = colors.green(`"${this.name}"`);
}
} catch (error) {
name = colors.yellow(`couldn't get name`);
}
let reference;
try {
if (this.directory) {
reference = colors.blue(Thing.getReference(this));
}
} catch (error) {
reference = colors.yellow(`couldn't get reference`);
}
return (
(name ? `${constructorName} ${name}` : `${constructorName}`) +
(reference ? ` (${reference})` : ''));
}
static getReference(thing) {
if (!thing.constructor[Thing.referenceType]) {
throw TypeError(`Passed Thing is ${thing.constructor.name}, which provides no [Thing.referenceType]`);
}
if (!thing.directory) {
throw TypeError(`Passed ${thing.constructor.name} is missing its directory`);
}
return `${thing.constructor[Thing.referenceType]}:${thing.directory}`;
}
static extendDocumentSpec(thingClass, subspec) {
const superspec = thingClass[Thing.yamlDocumentSpec];
const {
fields,
ignoredFields,
invalidFieldCombinations,
...restOfSubspec
} = subspec;
const newFields = Object.keys(fields ?? {});
return {
...superspec,
...restOfSubspec,
fields: {
...superspec.fields ?? {},
...fields,
},
ignoredFields:
(superspec.ignoredFields ?? [])
.filter(field => newFields.includes(field))
.concat(ignoredFields ?? []),
invalidFieldCombinations: [
...superspec.invalidFieldCombinations ?? [],
...invalidFieldCombinations ?? [],
],
};
}
}
|