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
115
116
117
118
|
export class ContractManager {
#registeredContracts = Object.create(null);
registerContract(name, description) {
ContractManager.validateContractDescription(description);
this.#registeredContracts[name] = description;
}
getContractHooks(name) {
return this.getContractInfo(name).hooks;
}
getContractInfo(name) {
// todo: cache
return this.#computeContractInfo(name);
}
#computeContractInfo(name) {
const description = this.#registeredContracts[name];
if (!description) {
throw new Error(`Contract ${name} not registered`);
}
let numArguments = 0;
const args = [];
const argumentGenerator = (function*() {
while (true) {
const argument = {type: 'argument', index: numArguments++, of: name}
args.push(argument);
yield argument;
}
})();
const hooks = [];
const subcontracts = {};
const structure = {};
const contextualizeHook = (args, {type, ...hook}) => {
switch (type) {
case 'argument':
return {type: 'argument', index: hook.index};
case 'selectPropertyPath': {
/*
switch (hook.object.type) {
case 'argument':
console.log('select argument', hook.object.index, '=', args[hook.object.index]);
return {type: 'selectPropertyPath', object: args[hook.object.index], path: hook.path};
case 'selectPropertyPath':
console.log('merge', hook.object.path, 'with', hook.path);
return {type: 'selectPropertyPath', object: args[hook.object.object.index], path: [...hook.object.path, ...hook.path]};
default:
throw new Error(`Can't contextualize unknown hook type OF OBJECT ${hook.object.type}`);
}
*/
const contextualizedObject = contextualizeHook(args, hook.object);
console.log(`contextualized property object:`, contextualizedObject);
switch (contextualizedObject.type) {
case 'argument':
return {type: 'selectPropertyPath', object: args[contextualizedObject.index], path: hook.path};
case 'selectPropertyPath':
return {type: 'selectPropertyPath', object: contextualizedObject.object, path: [...contextualizedObject.path, ...hook.path]};
}
}
default:
throw new Error(`Can't contextualize unknown hook type ${type}`);
}
};
const contractUtility = {
subcontract: (name, ...args) => {
const info = this.getContractInfo(name.startsWith('#') ? name.slice(1) : name);
for (const hook of info.hooks) {
hooks.push(contextualizeHook(args, hook));
}
return {type: 'subcontract', name, args};
},
provide: (properties) => {
Object.assign(structure, properties);
},
selectProperty: (object, property) => {
hooks.push(contextualizeHook(args, {type: 'selectPropertyPath', object, path: [property]}));
return {type: 'selectPropertyPath', object, path: [property]};
},
};
description.hook(contractUtility, argumentGenerator);
return {hooks, subcontracts, structure};
}
static validateContractDescription(description) {
// todo
}
}
const {default: {contracts}} = await import('./content/dependencies/generateAlbumTrackList.js');
const util = await import('util');
const manager = new ContractManager();
for (const [name, description] of Object.entries(contracts)) {
manager.registerContract(name, description);
}
const testContract = 'displayTrackSections';
for (const hook of manager.getContractHooks(testContract)) {
if (hook.type === 'selectPropertyPath') {
console.log(`- (${util.inspect(hook.object, {colors: true})}).${hook.path.join('.')}`);
} else {
console.log(`- ${hook}`);
}
}
// console.log(util.inspect(manager.getContractInfo(testContract).structure, {colors: true, depth: Infinity}));
|