@openmrs/esm-config 10.0.1-pre.5364 → 10.0.1-pre.5373
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/.turbo/turbo-build.log
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-config.d.ts","sourceRoot":"","sources":["../../src/module-config/module-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAuB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"module-config.d.ts","sourceRoot":"","sources":["../../src/module-config/module-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAuB,MAAM,UAAU,CAAC;AA+SxF;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,QAS1E;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAAC,UAAU,EAAE,MAAM,QAKhE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,QAKpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,QAK7D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,QAetF;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,SAAa,QAK9D;AAiCD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAMjF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAoBxD;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,UAKvG;AA6nBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,QAUjD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,SAUhC"}
|
|
@@ -7,12 +7,13 @@ import { configExtensionStore, configInternalStore, getConfigStore, getExtension
|
|
|
7
7
|
* Deep merge two objects, with right-side values taking precedence.
|
|
8
8
|
* Arrays are replaced entirely (not merged by index), matching the
|
|
9
9
|
* behavior of ramda's mergeDeepRight.
|
|
10
|
-
*/ function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
*/ function replaceArrays(_objValue, srcValue) {
|
|
11
|
+
if (Array.isArray(srcValue)) {
|
|
12
|
+
return srcValue;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function mergeDeepReplace(left, right) {
|
|
16
|
+
return mergeWith({}, left, right, replaceArrays);
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
19
|
* Store setup
|
|
@@ -43,8 +44,9 @@ const configSubscriptions = [];
|
|
|
43
44
|
const tempConfigState = temporaryConfigStore.getState();
|
|
44
45
|
const extensionState = configExtensionStore.getState();
|
|
45
46
|
computeModuleConfig(configState, tempConfigState);
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
const slotConfigs = getExtensionSlotConfigs(configState, tempConfigState);
|
|
48
|
+
computeExtensionSlotConfigs(slotConfigs);
|
|
49
|
+
computeExtensionConfigs(configState, extensionState, tempConfigState, slotConfigs);
|
|
48
50
|
// Last, because a failure here must not skip the outputs above. Only kept current while the
|
|
49
51
|
// implementer tools are watching it; otherwise derived on read.
|
|
50
52
|
invalidateImplementerToolsConfig();
|
|
@@ -108,8 +110,7 @@ function computeModuleConfig(state, tempState) {
|
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
|
-
function computeExtensionSlotConfigs(
|
|
112
|
-
const slotConfigs = getExtensionSlotConfigs(state, tempState);
|
|
113
|
+
function computeExtensionSlotConfigs(slotConfigs) {
|
|
113
114
|
const slotStore = getExtensionSlotsConfigStore();
|
|
114
115
|
const oldSlots = slotStore.getState().slots;
|
|
115
116
|
const slots = {};
|
|
@@ -148,7 +149,7 @@ function computeImplementerToolsConfig(state, tempConfigState) {
|
|
|
148
149
|
implementerToolsConfigStore.setState(newState);
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
|
-
function computeExtensionConfigs(configState, extensionState, tempConfigState) {
|
|
152
|
+
function computeExtensionConfigs(configState, extensionState, tempConfigState, slotConfigs) {
|
|
152
153
|
const extensionsConfigStore = getExtensionsConfigStore();
|
|
153
154
|
const oldConfigs = extensionsConfigStore.getState().configs;
|
|
154
155
|
const configs = {};
|
|
@@ -157,7 +158,7 @@ function computeExtensionConfigs(configState, extensionState, tempConfigState) {
|
|
|
157
158
|
// We assume that the module schema has already been defined, since the extension
|
|
158
159
|
// it contains is mounted.
|
|
159
160
|
for (let extension of extensionState.mountedExtensions){
|
|
160
|
-
const config = computeExtensionConfig(extension.slotModuleName, extension.extensionModuleName, extension.slotName, extension.extensionId, configState, tempConfigState);
|
|
161
|
+
const config = computeExtensionConfig(extension.slotModuleName, extension.extensionModuleName, extension.slotName, extension.extensionId, configState, tempConfigState, slotConfigs[extension.slotName]);
|
|
161
162
|
const previous = oldConfigs[extension.slotName]?.[extension.extensionId];
|
|
162
163
|
// Entries keep their identity when the configuration behind them hasn't changed, so that
|
|
163
164
|
// mounting one extension doesn't hand every other mounted extension a new config object.
|
|
@@ -410,7 +411,8 @@ const extensionConfigCache = new Map();
|
|
|
410
411
|
* @param extensionModuleName The name of the module which defines the extension (and therefore the config schema)
|
|
411
412
|
* @param slotName The name of the extension slot where the extension is mounted
|
|
412
413
|
* @param extensionId The ID of the extension in its slot
|
|
413
|
-
|
|
414
|
+
* @param slotConfig The slot's configuration, gathered from every module that configures it
|
|
415
|
+
*/ function computeExtensionConfig(slotModuleName, extensionModuleName, slotName, extensionId, configState, tempConfigState, slotConfig) {
|
|
414
416
|
const extensionName = getExtensionNameFromId(extensionId);
|
|
415
417
|
const extensionConfigSchema = configState.schemas[extensionName];
|
|
416
418
|
// JSON string of these elements should be unique-per-extension
|
|
@@ -426,11 +428,13 @@ const extensionConfigCache = new Map();
|
|
|
426
428
|
}
|
|
427
429
|
const nameOfSchemaSource = extensionConfigSchema ? extensionName : extensionModuleName;
|
|
428
430
|
const providedConfigs = getProvidedConfigs(configState, tempConfigState);
|
|
431
|
+
const sharedOverride = slotConfig?.configure?.[extensionId] ?? {};
|
|
429
432
|
const slotModuleConfig = mergeConfigsFor(slotModuleName, providedConfigs);
|
|
430
433
|
const configOverride = slotModuleConfig?.extensionSlots?.[slotName]?.configure?.[extensionId] ?? {};
|
|
431
434
|
const extensionConfig = mergeConfigsFor(nameOfSchemaSource, providedConfigs);
|
|
432
435
|
const combinedConfig = mergeConfigs([
|
|
433
436
|
extensionConfig,
|
|
437
|
+
sharedOverride,
|
|
434
438
|
configOverride
|
|
435
439
|
]);
|
|
436
440
|
const schema = extensionConfigSchema ?? configState.schemas[extensionModuleName];
|
|
@@ -558,13 +562,26 @@ function getExtensionSlotConfigs(configState, tempConfigState) {
|
|
|
558
562
|
return obj;
|
|
559
563
|
}, {});
|
|
560
564
|
validateAllExtensionSlotConfigs(slotConfigPerModule);
|
|
561
|
-
const slotConfigs =
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
565
|
+
const slotConfigs = {};
|
|
566
|
+
const configureBlocks = {};
|
|
567
|
+
for (const configBySlotName of Object.values(slotConfigPerModule)){
|
|
568
|
+
for (const [slotName, config] of Object.entries(configBySlotName)){
|
|
569
|
+
slotConfigs[slotName] = config;
|
|
570
|
+
if (config.configure) {
|
|
571
|
+
var _configureBlocks, _slotName;
|
|
572
|
+
((_configureBlocks = configureBlocks)[_slotName = slotName] ?? (_configureBlocks[_slotName] = [])).push(config.configure);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
for (const [slotName, blocks] of Object.entries(configureBlocks)){
|
|
577
|
+
const configure = blocks.length === 1 ? blocks[0] : mergeConfigs(blocks);
|
|
578
|
+
if (slotConfigs[slotName].configure !== configure) {
|
|
579
|
+
slotConfigs[slotName] = {
|
|
580
|
+
...slotConfigs[slotName],
|
|
581
|
+
configure
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
}
|
|
568
585
|
return slotConfigs;
|
|
569
586
|
}
|
|
570
587
|
function validateAllExtensionSlotConfigs(slotConfigPerModule) {
|
|
@@ -687,8 +704,10 @@ function mergeConfigsFor(moduleName, allConfigs) {
|
|
|
687
704
|
const allConfigsForModule = allConfigs.map(({ [moduleName]: c })=>c).filter((c)=>!!c);
|
|
688
705
|
return mergeConfigs(allConfigsForModule);
|
|
689
706
|
}
|
|
690
|
-
|
|
691
|
-
|
|
707
|
+
/**
|
|
708
|
+
* Merges a whole list at once, accumulating into one destination.
|
|
709
|
+
*/ function mergeConfigs(configs) {
|
|
710
|
+
return configs.reduce((acc, config)=>mergeWith(acc, config, replaceArrays), {});
|
|
692
711
|
}
|
|
693
712
|
/**
|
|
694
713
|
* Recursively check the provided config tree to make sure that all
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-config",
|
|
3
|
-
"version": "10.0.1-pre.
|
|
3
|
+
"version": "10.0.1-pre.5373",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "A configuration library for the OpenMRS Single-Spa framework.",
|
|
6
6
|
"type": "module",
|
|
@@ -57,15 +57,15 @@
|
|
|
57
57
|
"lodash-es": "^4.17.21"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"@openmrs/esm-globals": "^10.0.1-pre.
|
|
61
|
-
"@openmrs/esm-state": "^10.0.1-pre.
|
|
62
|
-
"@openmrs/esm-utils": "^10.0.1-pre.
|
|
60
|
+
"@openmrs/esm-globals": "^10.0.1-pre.5373",
|
|
61
|
+
"@openmrs/esm-state": "^10.0.1-pre.5373",
|
|
62
|
+
"@openmrs/esm-utils": "^10.0.1-pre.5373",
|
|
63
63
|
"single-spa": "6.x"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@openmrs/esm-globals": "10.0.1-pre.
|
|
67
|
-
"@openmrs/esm-state": "10.0.1-pre.
|
|
68
|
-
"@openmrs/esm-utils": "10.0.1-pre.
|
|
66
|
+
"@openmrs/esm-globals": "10.0.1-pre.5373",
|
|
67
|
+
"@openmrs/esm-state": "10.0.1-pre.5373",
|
|
68
|
+
"@openmrs/esm-utils": "10.0.1-pre.5373",
|
|
69
69
|
"@swc/cli": "0.8.1",
|
|
70
70
|
"@swc/core": "1.15.21",
|
|
71
71
|
"@types/lodash-es": "^4.17.12",
|
|
@@ -1341,6 +1341,99 @@ describe('extension config', () => {
|
|
|
1341
1341
|
expect(console.error).not.toHaveBeenCalled();
|
|
1342
1342
|
});
|
|
1343
1343
|
|
|
1344
|
+
// The module that renders an extension owns the slot it opens, which need not be the module an
|
|
1345
|
+
// implementer configures that slot from.
|
|
1346
|
+
it("uses the 'configure' config written by a module other than the one owning the slot", () => {
|
|
1347
|
+
updateConfigExtensionStore('fooExt#id6');
|
|
1348
|
+
const configureConfig = {
|
|
1349
|
+
'ext-mod': { bar: 'qux' },
|
|
1350
|
+
'other-mod': {
|
|
1351
|
+
extensionSlots: {
|
|
1352
|
+
barSlot: {
|
|
1353
|
+
configure: { 'fooExt#id6': { baz: 'quiz' } },
|
|
1354
|
+
},
|
|
1355
|
+
},
|
|
1356
|
+
},
|
|
1357
|
+
};
|
|
1358
|
+
Config.provide(configureConfig);
|
|
1359
|
+
const result = getExtensionConfig('barSlot', 'fooExt#id6').getState().config;
|
|
1360
|
+
expect(result).toStrictEqual({
|
|
1361
|
+
bar: 'qux',
|
|
1362
|
+
baz: 'quiz',
|
|
1363
|
+
'Display conditions': { expression: undefined, privileges: [] },
|
|
1364
|
+
'Translation overrides': {},
|
|
1365
|
+
});
|
|
1366
|
+
expect(console.error).not.toHaveBeenCalled();
|
|
1367
|
+
});
|
|
1368
|
+
|
|
1369
|
+
it("prefers the slot-owning module's 'configure' config to another module's", () => {
|
|
1370
|
+
updateConfigExtensionStore('fooExt#id7');
|
|
1371
|
+
const configureConfig = {
|
|
1372
|
+
'slot-mod': {
|
|
1373
|
+
extensionSlots: {
|
|
1374
|
+
barSlot: {
|
|
1375
|
+
configure: { 'fooExt#id7': { baz: 'from-owner' } },
|
|
1376
|
+
},
|
|
1377
|
+
},
|
|
1378
|
+
},
|
|
1379
|
+
'other-mod': {
|
|
1380
|
+
extensionSlots: {
|
|
1381
|
+
barSlot: {
|
|
1382
|
+
configure: { 'fooExt#id7': { bar: 'from-other', baz: 'from-other' } },
|
|
1383
|
+
},
|
|
1384
|
+
},
|
|
1385
|
+
},
|
|
1386
|
+
};
|
|
1387
|
+
Config.provide(configureConfig);
|
|
1388
|
+
const result = getExtensionConfig('barSlot', 'fooExt#id7').getState().config;
|
|
1389
|
+
expect(result).toStrictEqual({
|
|
1390
|
+
bar: 'from-other',
|
|
1391
|
+
baz: 'from-owner',
|
|
1392
|
+
'Display conditions': { expression: undefined, privileges: [] },
|
|
1393
|
+
'Translation overrides': {},
|
|
1394
|
+
});
|
|
1395
|
+
expect(console.error).not.toHaveBeenCalled();
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
it("gathers 'configure' from every module but leaves 'add' to the last one to declare the slot", () => {
|
|
1399
|
+
updateConfigExtensionStore('fooExt#id8');
|
|
1400
|
+
Config.provide({
|
|
1401
|
+
'first-mod': {
|
|
1402
|
+
extensionSlots: {
|
|
1403
|
+
barSlot: { add: ['fooExt#unwanted'], configure: { 'fooExt#id8': { bar: 'from-first' } } },
|
|
1404
|
+
},
|
|
1405
|
+
},
|
|
1406
|
+
'last-mod': {
|
|
1407
|
+
extensionSlots: {
|
|
1408
|
+
barSlot: { add: ['fooExt#id8'], configure: { 'fooExt#id8': { baz: 'from-last' } } },
|
|
1409
|
+
},
|
|
1410
|
+
},
|
|
1411
|
+
});
|
|
1412
|
+
|
|
1413
|
+
// Composing `add` across modules would resurrect entries another module had removed, so the
|
|
1414
|
+
// last module to name the slot still supplies it whole.
|
|
1415
|
+
expect(getExtensionSlotsConfigStore().getState().slots['barSlot'].config.add).toStrictEqual(['fooExt#id8']);
|
|
1416
|
+
expect(getExtensionConfig('barSlot', 'fooExt#id8').getState().config).toStrictEqual({
|
|
1417
|
+
bar: 'from-first',
|
|
1418
|
+
baz: 'from-last',
|
|
1419
|
+
'Display conditions': { expression: undefined, privileges: [] },
|
|
1420
|
+
'Translation overrides': {},
|
|
1421
|
+
});
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
it('releases a cross-module configure when the temporary config that set it is cleared', () => {
|
|
1425
|
+
updateConfigExtensionStore('fooExt#id9');
|
|
1426
|
+
temporaryConfigStore.setState({
|
|
1427
|
+
config: { 'other-mod': { extensionSlots: { barSlot: { configure: { 'fooExt#id9': { baz: 'temporary' } } } } } },
|
|
1428
|
+
});
|
|
1429
|
+
expect(getExtensionConfig('barSlot', 'fooExt#id9').getState().config.baz).toBe('temporary');
|
|
1430
|
+
|
|
1431
|
+
// The derived slot config is not part of the extension config cache key, so a released override
|
|
1432
|
+
// is only picked up because that cache also keys on the temporary config it was derived from.
|
|
1433
|
+
temporaryConfigStore.setState({ config: {} });
|
|
1434
|
+
expect(getExtensionConfig('barSlot', 'fooExt#id9').getState().config.baz).toBe('bazzy');
|
|
1435
|
+
});
|
|
1436
|
+
|
|
1344
1437
|
it('validates the extension configure config, with module config schema', () => {
|
|
1345
1438
|
updateConfigExtensionStore('fooExt#id1');
|
|
1346
1439
|
const badConfig = {
|
|
@@ -32,12 +32,14 @@ import { type TemporaryConfigStore } from '..';
|
|
|
32
32
|
* Arrays are replaced entirely (not merged by index), matching the
|
|
33
33
|
* behavior of ramda's mergeDeepRight.
|
|
34
34
|
*/
|
|
35
|
+
function replaceArrays(_objValue: unknown, srcValue: unknown) {
|
|
36
|
+
if (Array.isArray(srcValue)) {
|
|
37
|
+
return srcValue;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
function mergeDeepReplace<T extends object>(left: T, right: Partial<T>): T {
|
|
36
|
-
return mergeWith({}, left, right,
|
|
37
|
-
if (Array.isArray(srcValue)) {
|
|
38
|
-
return srcValue;
|
|
39
|
-
}
|
|
40
|
-
}) as T;
|
|
42
|
+
return mergeWith({}, left, right, replaceArrays) as T;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
/**
|
|
@@ -73,8 +75,11 @@ function recomputeAllConfigs() {
|
|
|
73
75
|
const extensionState = configExtensionStore.getState();
|
|
74
76
|
|
|
75
77
|
computeModuleConfig(configState, tempConfigState);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
|
|
79
|
+
const slotConfigs = getExtensionSlotConfigs(configState, tempConfigState);
|
|
80
|
+
computeExtensionSlotConfigs(slotConfigs);
|
|
81
|
+
computeExtensionConfigs(configState, extensionState, tempConfigState, slotConfigs);
|
|
82
|
+
|
|
78
83
|
// Last, because a failure here must not skip the outputs above. Only kept current while the
|
|
79
84
|
// implementer tools are watching it; otherwise derived on read.
|
|
80
85
|
invalidateImplementerToolsConfig();
|
|
@@ -185,8 +190,7 @@ function computeModuleConfig(state: ConfigInternalStore, tempState: TemporaryCon
|
|
|
185
190
|
}
|
|
186
191
|
}
|
|
187
192
|
|
|
188
|
-
function computeExtensionSlotConfigs(
|
|
189
|
-
const slotConfigs = getExtensionSlotConfigs(state, tempState);
|
|
193
|
+
function computeExtensionSlotConfigs(slotConfigs: Record<string, ExtensionSlotConfig>) {
|
|
190
194
|
const slotStore = getExtensionSlotsConfigStore();
|
|
191
195
|
const oldSlots = slotStore.getState().slots;
|
|
192
196
|
const slots: ExtensionSlotsConfigStore['slots'] = {};
|
|
@@ -228,6 +232,7 @@ function computeExtensionConfigs(
|
|
|
228
232
|
configState: ConfigInternalStore,
|
|
229
233
|
extensionState: ConfigExtensionStore,
|
|
230
234
|
tempConfigState: TemporaryConfigStore,
|
|
235
|
+
slotConfigs: Record<string, ExtensionSlotConfig>,
|
|
231
236
|
) {
|
|
232
237
|
const extensionsConfigStore = getExtensionsConfigStore();
|
|
233
238
|
const oldConfigs = extensionsConfigStore.getState().configs;
|
|
@@ -245,6 +250,7 @@ function computeExtensionConfigs(
|
|
|
245
250
|
extension.extensionId,
|
|
246
251
|
configState,
|
|
247
252
|
tempConfigState,
|
|
253
|
+
slotConfigs[extension.slotName],
|
|
248
254
|
);
|
|
249
255
|
|
|
250
256
|
const previous = oldConfigs[extension.slotName]?.[extension.extensionId];
|
|
@@ -555,6 +561,7 @@ const extensionConfigCache = new Map<string, ExtensionConfigCacheEntry>();
|
|
|
555
561
|
* @param extensionModuleName The name of the module which defines the extension (and therefore the config schema)
|
|
556
562
|
* @param slotName The name of the extension slot where the extension is mounted
|
|
557
563
|
* @param extensionId The ID of the extension in its slot
|
|
564
|
+
* @param slotConfig The slot's configuration, gathered from every module that configures it
|
|
558
565
|
*/
|
|
559
566
|
function computeExtensionConfig(
|
|
560
567
|
slotModuleName: string,
|
|
@@ -563,6 +570,7 @@ function computeExtensionConfig(
|
|
|
563
570
|
extensionId: string,
|
|
564
571
|
configState: ConfigInternalStore,
|
|
565
572
|
tempConfigState: TemporaryConfigStore,
|
|
573
|
+
slotConfig: ExtensionSlotConfig | undefined,
|
|
566
574
|
) {
|
|
567
575
|
const extensionName = getExtensionNameFromId(extensionId);
|
|
568
576
|
const extensionConfigSchema = configState.schemas[extensionName];
|
|
@@ -582,10 +590,11 @@ function computeExtensionConfig(
|
|
|
582
590
|
|
|
583
591
|
const nameOfSchemaSource = extensionConfigSchema ? extensionName : extensionModuleName;
|
|
584
592
|
const providedConfigs = getProvidedConfigs(configState, tempConfigState);
|
|
593
|
+
const sharedOverride = slotConfig?.configure?.[extensionId] ?? {};
|
|
585
594
|
const slotModuleConfig = mergeConfigsFor(slotModuleName, providedConfigs);
|
|
586
595
|
const configOverride = slotModuleConfig?.extensionSlots?.[slotName]?.configure?.[extensionId] ?? {};
|
|
587
596
|
const extensionConfig = mergeConfigsFor(nameOfSchemaSource, providedConfigs);
|
|
588
|
-
const combinedConfig = mergeConfigs([extensionConfig, configOverride]);
|
|
597
|
+
const combinedConfig = mergeConfigs([extensionConfig, sharedOverride, configOverride]);
|
|
589
598
|
const schema = extensionConfigSchema ?? configState.schemas[extensionModuleName];
|
|
590
599
|
validateStructure(schema, combinedConfig, nameOfSchemaSource);
|
|
591
600
|
const config = setDefaults(schema, combinedConfig);
|
|
@@ -739,11 +748,30 @@ function getExtensionSlotConfigs(
|
|
|
739
748
|
},
|
|
740
749
|
{},
|
|
741
750
|
);
|
|
751
|
+
|
|
742
752
|
validateAllExtensionSlotConfigs(slotConfigPerModule);
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
753
|
+
|
|
754
|
+
const slotConfigs: Record<string, ExtensionSlotConfig> = {};
|
|
755
|
+
const configureBlocks: Record<string, Array<NonNullable<ExtensionSlotConfig['configure']>>> = {};
|
|
756
|
+
|
|
757
|
+
for (const configBySlotName of Object.values(slotConfigPerModule)) {
|
|
758
|
+
for (const [slotName, config] of Object.entries(configBySlotName)) {
|
|
759
|
+
slotConfigs[slotName] = config;
|
|
760
|
+
|
|
761
|
+
if (config.configure) {
|
|
762
|
+
(configureBlocks[slotName] ??= []).push(config.configure);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
for (const [slotName, blocks] of Object.entries(configureBlocks)) {
|
|
768
|
+
const configure = blocks.length === 1 ? blocks[0] : (mergeConfigs(blocks) as ExtensionSlotConfig['configure']);
|
|
769
|
+
|
|
770
|
+
if (slotConfigs[slotName].configure !== configure) {
|
|
771
|
+
slotConfigs[slotName] = { ...slotConfigs[slotName], configure };
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
747
775
|
return slotConfigs;
|
|
748
776
|
}
|
|
749
777
|
|
|
@@ -901,8 +929,11 @@ function mergeConfigsFor(moduleName: string, allConfigs: Array<Config>): ConfigO
|
|
|
901
929
|
return mergeConfigs(allConfigsForModule);
|
|
902
930
|
}
|
|
903
931
|
|
|
932
|
+
/**
|
|
933
|
+
* Merges a whole list at once, accumulating into one destination.
|
|
934
|
+
*/
|
|
904
935
|
function mergeConfigs(configs: Array<Config>) {
|
|
905
|
-
return configs.reduce<Config>((acc, config) =>
|
|
936
|
+
return configs.reduce<Config>((acc, config) => mergeWith(acc, config, replaceArrays), {});
|
|
906
937
|
}
|
|
907
938
|
|
|
908
939
|
/**
|