@cratis/scene.engine 0.1.0
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/BindingResolver.ts +11 -0
- package/ComponentResolution.ts +22 -0
- package/PackageSelection.ts +87 -0
- package/README.md +40 -0
- package/Renderer.ts +33 -0
- package/aggregateContributions.ts +40 -0
- package/buildStarterProfile.ts +21 -0
- package/computeSizeClass.ts +30 -0
- package/dist/cjs/PackageSelection.js +15 -0
- package/dist/cjs/PackageSelection.js.map +1 -0
- package/dist/cjs/aggregateContributions.js +37 -0
- package/dist/cjs/aggregateContributions.js.map +1 -0
- package/dist/cjs/buildStarterProfile.js +27 -0
- package/dist/cjs/buildStarterProfile.js.map +1 -0
- package/dist/cjs/computeSizeClass.js +31 -0
- package/dist/cjs/computeSizeClass.js.map +1 -0
- package/dist/cjs/elementKind.js +26 -0
- package/dist/cjs/elementKind.js.map +1 -0
- package/dist/cjs/evaluateFlowArrangement.js +31 -0
- package/dist/cjs/evaluateFlowArrangement.js.map +1 -0
- package/dist/cjs/evaluateFreeformArrangement.js +16 -0
- package/dist/cjs/evaluateFreeformArrangement.js.map +1 -0
- package/dist/cjs/evaluateFreeformSlotArrangement.js +18 -0
- package/dist/cjs/evaluateFreeformSlotArrangement.js.map +1 -0
- package/dist/cjs/flowNodeKind.js +40 -0
- package/dist/cjs/flowNodeKind.js.map +1 -0
- package/dist/cjs/incompatibleStarterThemes.js +29 -0
- package/dist/cjs/incompatibleStarterThemes.js.map +1 -0
- package/dist/cjs/index.js +59 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/packageCatalog.js +63 -0
- package/dist/cjs/packageCatalog.js.map +1 -0
- package/dist/cjs/packageVersionRange.js +113 -0
- package/dist/cjs/packageVersionRange.js.map +1 -0
- package/dist/cjs/renderElement.js +39 -0
- package/dist/cjs/renderElement.js.map +1 -0
- package/dist/cjs/resolveComponentName.js +67 -0
- package/dist/cjs/resolveComponentName.js.map +1 -0
- package/dist/cjs/resolvePackageDependencies.js +149 -0
- package/dist/cjs/resolvePackageDependencies.js.map +1 -0
- package/dist/cjs/resolveScreenTemplates.js +136 -0
- package/dist/cjs/resolveScreenTemplates.js.map +1 -0
- package/dist/cjs/themeCompatibility.js +45 -0
- package/dist/cjs/themeCompatibility.js.map +1 -0
- package/dist/esm/BindingResolver.d.ts +3 -0
- package/dist/esm/BindingResolver.d.ts.map +1 -0
- package/dist/esm/BindingResolver.js +2 -0
- package/dist/esm/BindingResolver.js.map +1 -0
- package/dist/esm/ComponentResolution.d.ts +6 -0
- package/dist/esm/ComponentResolution.d.ts.map +1 -0
- package/dist/esm/ComponentResolution.js +2 -0
- package/dist/esm/ComponentResolution.js.map +1 -0
- package/dist/esm/PackageSelection.d.ts +19 -0
- package/dist/esm/PackageSelection.d.ts.map +1 -0
- package/dist/esm/PackageSelection.js +13 -0
- package/dist/esm/PackageSelection.js.map +1 -0
- package/dist/esm/Renderer.d.ts +8 -0
- package/dist/esm/Renderer.d.ts.map +1 -0
- package/dist/esm/Renderer.js +2 -0
- package/dist/esm/Renderer.js.map +1 -0
- package/dist/esm/aggregateContributions.d.ts +3 -0
- package/dist/esm/aggregateContributions.d.ts.map +1 -0
- package/dist/esm/aggregateContributions.js +35 -0
- package/dist/esm/aggregateContributions.js.map +1 -0
- package/dist/esm/buildStarterProfile.d.ts +3 -0
- package/dist/esm/buildStarterProfile.d.ts.map +1 -0
- package/dist/esm/buildStarterProfile.js +25 -0
- package/dist/esm/buildStarterProfile.js.map +1 -0
- package/dist/esm/computeSizeClass.d.ts +5 -0
- package/dist/esm/computeSizeClass.d.ts.map +1 -0
- package/dist/esm/computeSizeClass.js +27 -0
- package/dist/esm/computeSizeClass.js.map +1 -0
- package/dist/esm/elementKind.d.ts +6 -0
- package/dist/esm/elementKind.d.ts.map +1 -0
- package/dist/esm/elementKind.js +21 -0
- package/dist/esm/elementKind.js.map +1 -0
- package/dist/esm/evaluateFlowArrangement.d.ts +3 -0
- package/dist/esm/evaluateFlowArrangement.d.ts.map +1 -0
- package/dist/esm/evaluateFlowArrangement.js +29 -0
- package/dist/esm/evaluateFlowArrangement.js.map +1 -0
- package/dist/esm/evaluateFreeformArrangement.d.ts +3 -0
- package/dist/esm/evaluateFreeformArrangement.d.ts.map +1 -0
- package/dist/esm/evaluateFreeformArrangement.js +14 -0
- package/dist/esm/evaluateFreeformArrangement.js.map +1 -0
- package/dist/esm/evaluateFreeformSlotArrangement.d.ts +3 -0
- package/dist/esm/evaluateFreeformSlotArrangement.d.ts.map +1 -0
- package/dist/esm/evaluateFreeformSlotArrangement.js +16 -0
- package/dist/esm/evaluateFreeformSlotArrangement.js.map +1 -0
- package/dist/esm/flowNodeKind.d.ts +8 -0
- package/dist/esm/flowNodeKind.d.ts.map +1 -0
- package/dist/esm/flowNodeKind.js +33 -0
- package/dist/esm/flowNodeKind.js.map +1 -0
- package/dist/esm/for_aggregateContributions/when_aggregating_by_contribution_point.d.ts +2 -0
- package/dist/esm/for_aggregateContributions/when_aggregating_by_contribution_point.d.ts.map +1 -0
- package/dist/esm/for_aggregateContributions/when_aggregating_by_contribution_point.js +23 -0
- package/dist/esm/for_aggregateContributions/when_aggregating_by_contribution_point.js.map +1 -0
- package/dist/esm/for_buildStarterProfile/when_checking_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_buildStarterProfile/when_checking_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_buildStarterProfile/when_checking_against_the_shared_fixture_corpus.js +16 -0
- package/dist/esm/for_buildStarterProfile/when_checking_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/for_computeSizeClass/when_computing_the_size_class.d.ts +2 -0
- package/dist/esm/for_computeSizeClass/when_computing_the_size_class.d.ts.map +1 -0
- package/dist/esm/for_computeSizeClass/when_computing_the_size_class.js +17 -0
- package/dist/esm/for_computeSizeClass/when_computing_the_size_class.js.map +1 -0
- package/dist/esm/for_evaluateFlowArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_evaluateFlowArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_evaluateFlowArrangement/when_evaluating_against_the_shared_fixture_corpus.js +43 -0
- package/dist/esm/for_evaluateFlowArrangement/when_evaluating_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/for_evaluateFreeformArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_evaluateFreeformArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_evaluateFreeformArrangement/when_evaluating_against_the_shared_fixture_corpus.js +46 -0
- package/dist/esm/for_evaluateFreeformArrangement/when_evaluating_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/for_evaluateFreeformSlotArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_evaluateFreeformSlotArrangement/when_evaluating_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_evaluateFreeformSlotArrangement/when_evaluating_against_the_shared_fixture_corpus.js +25 -0
- package/dist/esm/for_evaluateFreeformSlotArrangement/when_evaluating_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/for_incompatibleStarterThemes/when_checking_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_incompatibleStarterThemes/when_checking_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_incompatibleStarterThemes/when_checking_against_the_shared_fixture_corpus.js +17 -0
- package/dist/esm/for_incompatibleStarterThemes/when_checking_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/for_packageCatalog/when_querying_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_packageCatalog/when_querying_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_packageCatalog/when_querying_against_the_shared_fixture_corpus.js +22 -0
- package/dist/esm/for_packageCatalog/when_querying_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/for_packageDependencies/fixtures.d.ts +38 -0
- package/dist/esm/for_packageDependencies/fixtures.d.ts.map +1 -0
- package/dist/esm/for_packageDependencies/fixtures.js +5 -0
- package/dist/esm/for_packageDependencies/fixtures.js.map +1 -0
- package/dist/esm/for_packageDependencies/when_resolving_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_packageDependencies/when_resolving_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_packageDependencies/when_resolving_against_the_shared_fixture_corpus.js +29 -0
- package/dist/esm/for_packageDependencies/when_resolving_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/for_packageVersionRange/when_checking_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_packageVersionRange/when_checking_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_packageVersionRange/when_checking_against_the_shared_fixture_corpus.js +10 -0
- package/dist/esm/for_packageVersionRange/when_checking_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/for_renderElement/when_rendering_a_mixed_tree.d.ts +2 -0
- package/dist/esm/for_renderElement/when_rendering_a_mixed_tree.d.ts.map +1 -0
- package/dist/esm/for_renderElement/when_rendering_a_mixed_tree.js +46 -0
- package/dist/esm/for_renderElement/when_rendering_a_mixed_tree.js.map +1 -0
- package/dist/esm/for_resolveComponentName/when_resolving_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_resolveComponentName/when_resolving_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_resolveComponentName/when_resolving_against_the_shared_fixture_corpus.js +20 -0
- package/dist/esm/for_resolveComponentName/when_resolving_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/for_resolveScreenTemplates/when_resolving_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_resolveScreenTemplates/when_resolving_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_resolveScreenTemplates/when_resolving_against_the_shared_fixture_corpus.js +30 -0
- package/dist/esm/for_resolveScreenTemplates/when_resolving_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/for_themeCompatibility/when_checking_against_the_shared_fixture_corpus.d.ts +2 -0
- package/dist/esm/for_themeCompatibility/when_checking_against_the_shared_fixture_corpus.d.ts.map +1 -0
- package/dist/esm/for_themeCompatibility/when_checking_against_the_shared_fixture_corpus.js +18 -0
- package/dist/esm/for_themeCompatibility/when_checking_against_the_shared_fixture_corpus.js.map +1 -0
- package/dist/esm/incompatibleStarterThemes.d.ts +3 -0
- package/dist/esm/incompatibleStarterThemes.d.ts.map +1 -0
- package/dist/esm/incompatibleStarterThemes.js +27 -0
- package/dist/esm/incompatibleStarterThemes.js.map +1 -0
- package/dist/esm/index.d.ts +21 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/packageCatalog.d.ts +7 -0
- package/dist/esm/packageCatalog.d.ts.map +1 -0
- package/dist/esm/packageCatalog.js +57 -0
- package/dist/esm/packageCatalog.js.map +1 -0
- package/dist/esm/packageVersionRange.d.ts +2 -0
- package/dist/esm/packageVersionRange.d.ts.map +1 -0
- package/dist/esm/packageVersionRange.js +111 -0
- package/dist/esm/packageVersionRange.js.map +1 -0
- package/dist/esm/renderElement.d.ts +5 -0
- package/dist/esm/renderElement.d.ts.map +1 -0
- package/dist/esm/renderElement.js +37 -0
- package/dist/esm/renderElement.js.map +1 -0
- package/dist/esm/resolveComponentName.d.ts +7 -0
- package/dist/esm/resolveComponentName.d.ts.map +1 -0
- package/dist/esm/resolveComponentName.js +63 -0
- package/dist/esm/resolveComponentName.js.map +1 -0
- package/dist/esm/resolvePackageDependencies.d.ts +4 -0
- package/dist/esm/resolvePackageDependencies.d.ts.map +1 -0
- package/dist/esm/resolvePackageDependencies.js +147 -0
- package/dist/esm/resolvePackageDependencies.js.map +1 -0
- package/dist/esm/resolveScreenTemplates.d.ts +20 -0
- package/dist/esm/resolveScreenTemplates.d.ts.map +1 -0
- package/dist/esm/resolveScreenTemplates.js +133 -0
- package/dist/esm/resolveScreenTemplates.js.map +1 -0
- package/dist/esm/themeCompatibility.d.ts +5 -0
- package/dist/esm/themeCompatibility.d.ts.map +1 -0
- package/dist/esm/themeCompatibility.js +41 -0
- package/dist/esm/themeCompatibility.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -0
- package/elementKind.ts +26 -0
- package/evaluateFlowArrangement.ts +35 -0
- package/evaluateFreeformArrangement.ts +15 -0
- package/evaluateFreeformSlotArrangement.ts +17 -0
- package/flowNodeKind.ts +38 -0
- package/for_aggregateContributions/when_aggregating_by_contribution_point.ts +31 -0
- package/for_buildStarterProfile/when_checking_against_the_shared_fixture_corpus.ts +32 -0
- package/for_computeSizeClass/when_computing_the_size_class.ts +23 -0
- package/for_evaluateFlowArrangement/when_evaluating_against_the_shared_fixture_corpus.ts +67 -0
- package/for_evaluateFreeformArrangement/when_evaluating_against_the_shared_fixture_corpus.ts +68 -0
- package/for_evaluateFreeformSlotArrangement/when_evaluating_against_the_shared_fixture_corpus.ts +47 -0
- package/for_incompatibleStarterThemes/when_checking_against_the_shared_fixture_corpus.ts +34 -0
- package/for_packageCatalog/when_querying_against_the_shared_fixture_corpus.ts +26 -0
- package/for_packageDependencies/fixtures.ts +44 -0
- package/for_packageDependencies/when_resolving_against_the_shared_fixture_corpus.ts +39 -0
- package/for_packageVersionRange/when_checking_against_the_shared_fixture_corpus.ts +13 -0
- package/for_renderElement/when_rendering_a_mixed_tree.ts +58 -0
- package/for_resolveComponentName/when_resolving_against_the_shared_fixture_corpus.ts +36 -0
- package/for_resolveScreenTemplates/when_resolving_against_the_shared_fixture_corpus.ts +49 -0
- package/for_themeCompatibility/when_checking_against_the_shared_fixture_corpus.ts +37 -0
- package/incompatibleStarterThemes.ts +24 -0
- package/index.ts +23 -0
- package/package.json +45 -0
- package/packageCatalog.ts +65 -0
- package/packageVersionRange.ts +90 -0
- package/renderElement.ts +40 -0
- package/resolveComponentName.ts +66 -0
- package/resolvePackageDependencies.ts +156 -0
- package/resolveScreenTemplates.ts +191 -0
- package/themeCompatibility.ts +46 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { isVersionSatisfiedBy } from './packageVersionRange.js';
|
|
2
|
+
|
|
3
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
4
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
5
|
+
/**
|
|
6
|
+
* Expands a chosen set of package names into the complete, correctly ordered list a {@link UiProfile}
|
|
7
|
+
* needs — the runtime half. The C# twin in `Cratis.Scene.Engine` implements the same algorithm for
|
|
8
|
+
* Studio's package picker and Stage's build; both are asserted against the same shared fixture corpus so
|
|
9
|
+
* they cannot drift apart.
|
|
10
|
+
*
|
|
11
|
+
* This is a different concern from `resolveComponentName`, which resolves one component *name* against
|
|
12
|
+
* an already-decided package list. This decides what that list is.
|
|
13
|
+
*
|
|
14
|
+
* Ordering is a stable topological sort: among packages whose dependencies are equally satisfied, the one
|
|
15
|
+
* the caller named first comes first, and packages pulled in transitively follow in the order they were
|
|
16
|
+
* discovered. When a cycle makes ordering impossible, the packages involved are reported in `cycles` and
|
|
17
|
+
* appended in discovery order rather than dropped.
|
|
18
|
+
*/ function resolvePackageDependencies(selected, catalog) {
|
|
19
|
+
const index = new Map(catalog.map((scenePackage)=>[
|
|
20
|
+
scenePackage.name,
|
|
21
|
+
scenePackage
|
|
22
|
+
]));
|
|
23
|
+
const discovered = [];
|
|
24
|
+
const seen = new Set(selected);
|
|
25
|
+
const missing = [];
|
|
26
|
+
const versionConflicts = [];
|
|
27
|
+
const pending = [
|
|
28
|
+
...selected
|
|
29
|
+
];
|
|
30
|
+
while(pending.length > 0){
|
|
31
|
+
const name = pending.shift();
|
|
32
|
+
discovered.push(name);
|
|
33
|
+
const scenePackage = index.get(name);
|
|
34
|
+
if (!scenePackage) continue;
|
|
35
|
+
for (const dependency of scenePackage.dependencies){
|
|
36
|
+
const dependedOn = index.get(dependency.name);
|
|
37
|
+
if (!dependedOn) {
|
|
38
|
+
missing.push({
|
|
39
|
+
package: scenePackage.name,
|
|
40
|
+
dependsOn: dependency.name
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (!isVersionSatisfiedBy(dependedOn.version, dependency.versionRange)) {
|
|
45
|
+
versionConflicts.push({
|
|
46
|
+
package: scenePackage.name,
|
|
47
|
+
dependsOn: dependency.name,
|
|
48
|
+
requiredRange: dependency.versionRange,
|
|
49
|
+
actualVersion: dependedOn.version
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (!seen.has(dependency.name)) {
|
|
53
|
+
seen.add(dependency.name);
|
|
54
|
+
pending.push(dependency.name);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const cycles = findCycles(discovered, index);
|
|
59
|
+
const packages = topologicallyOrder(discovered, index);
|
|
60
|
+
const chosen = new Set(selected);
|
|
61
|
+
return {
|
|
62
|
+
packages,
|
|
63
|
+
added: packages.filter((name)=>!chosen.has(name)),
|
|
64
|
+
missing,
|
|
65
|
+
versionConflicts,
|
|
66
|
+
cycles
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function dependenciesWithin(name, index, within) {
|
|
70
|
+
const scenePackage = index.get(name);
|
|
71
|
+
if (!scenePackage) return [];
|
|
72
|
+
const names = scenePackage.dependencies.map((dependency)=>dependency.name).filter((dependency)=>within.has(dependency));
|
|
73
|
+
return [
|
|
74
|
+
...new Set(names)
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Orders an already-closed set of package names so each follows the packages it depends on.
|
|
79
|
+
*/ function topologicallyOrder(names, index) {
|
|
80
|
+
const within = new Set(names);
|
|
81
|
+
const remaining = new Map(names.map((name)=>[
|
|
82
|
+
name,
|
|
83
|
+
dependenciesWithin(name, index, within).length
|
|
84
|
+
]));
|
|
85
|
+
const ordered = [];
|
|
86
|
+
const placed = new Set();
|
|
87
|
+
let progressed = true;
|
|
88
|
+
while(progressed){
|
|
89
|
+
progressed = false;
|
|
90
|
+
for (const name of names){
|
|
91
|
+
if (placed.has(name) || remaining.get(name) !== 0) continue;
|
|
92
|
+
ordered.push(name);
|
|
93
|
+
placed.add(name);
|
|
94
|
+
progressed = true;
|
|
95
|
+
for (const other of names){
|
|
96
|
+
if (placed.has(other)) continue;
|
|
97
|
+
if (dependenciesWithin(other, index, within).includes(name)) {
|
|
98
|
+
remaining.set(other, remaining.get(other) - 1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Anything left is inside a cycle, or downstream of one. It is reported through `cycles`; keeping it
|
|
104
|
+
// in the list means a package picker never silently loses a package it was told about.
|
|
105
|
+
ordered.push(...names.filter((name)=>!placed.has(name)));
|
|
106
|
+
return ordered;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Finds every dependency cycle, walking depth-first from each package in discovery order so the result
|
|
110
|
+
* is deterministic. A cycle is recorded once, keyed by the set of packages in it.
|
|
111
|
+
*/ function findCycles(names, index) {
|
|
112
|
+
const within = new Set(names);
|
|
113
|
+
const cycles = [];
|
|
114
|
+
const recorded = new Set();
|
|
115
|
+
const path = [];
|
|
116
|
+
const onPath = new Set();
|
|
117
|
+
const explored = new Set();
|
|
118
|
+
const walk = (name)=>{
|
|
119
|
+
if (onPath.has(name)) {
|
|
120
|
+
const cycle = path.slice(path.indexOf(name));
|
|
121
|
+
const key = [
|
|
122
|
+
...cycle
|
|
123
|
+
].sort().join(' ');
|
|
124
|
+
if (!recorded.has(key)) {
|
|
125
|
+
recorded.add(key);
|
|
126
|
+
cycles.push(cycle);
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (explored.has(name)) return;
|
|
131
|
+
explored.add(name);
|
|
132
|
+
path.push(name);
|
|
133
|
+
onPath.add(name);
|
|
134
|
+
for (const dependency of dependenciesWithin(name, index, within)){
|
|
135
|
+
walk(dependency);
|
|
136
|
+
}
|
|
137
|
+
onPath.delete(name);
|
|
138
|
+
path.pop();
|
|
139
|
+
};
|
|
140
|
+
for (const name of names){
|
|
141
|
+
walk(name);
|
|
142
|
+
}
|
|
143
|
+
return cycles;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { resolvePackageDependencies };
|
|
147
|
+
//# sourceMappingURL=resolvePackageDependencies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolvePackageDependencies.js","sources":["../../resolvePackageDependencies.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { ScenePackage } from '@cratis/scene.model';\nimport { MissingPackageDependency, PackageSelection, PackageVersionConflict } from './PackageSelection';\nimport { isVersionSatisfiedBy } from './packageVersionRange';\n\n/**\n * Expands a chosen set of package names into the complete, correctly ordered list a {@link UiProfile}\n * needs — the runtime half. The C# twin in `Cratis.Scene.Engine` implements the same algorithm for\n * Studio's package picker and Stage's build; both are asserted against the same shared fixture corpus so\n * they cannot drift apart.\n *\n * This is a different concern from `resolveComponentName`, which resolves one component *name* against\n * an already-decided package list. This decides what that list is.\n *\n * Ordering is a stable topological sort: among packages whose dependencies are equally satisfied, the one\n * the caller named first comes first, and packages pulled in transitively follow in the order they were\n * discovered. When a cycle makes ordering impossible, the packages involved are reported in `cycles` and\n * appended in discovery order rather than dropped.\n */\nexport function resolvePackageDependencies(selected: string[], catalog: ScenePackage[]): PackageSelection {\n const index = new Map(catalog.map((scenePackage) => [scenePackage.name, scenePackage]));\n const discovered: string[] = [];\n const seen = new Set<string>(selected);\n const missing: MissingPackageDependency[] = [];\n const versionConflicts: PackageVersionConflict[] = [];\n\n const pending = [...selected];\n while (pending.length > 0) {\n const name = pending.shift()!;\n discovered.push(name);\n\n const scenePackage = index.get(name);\n if (!scenePackage) continue;\n\n for (const dependency of scenePackage.dependencies) {\n const dependedOn = index.get(dependency.name);\n if (!dependedOn) {\n missing.push({ package: scenePackage.name, dependsOn: dependency.name });\n continue;\n }\n\n if (!isVersionSatisfiedBy(dependedOn.version, dependency.versionRange)) {\n versionConflicts.push({\n package: scenePackage.name,\n dependsOn: dependency.name,\n requiredRange: dependency.versionRange!,\n actualVersion: dependedOn.version,\n });\n }\n\n if (!seen.has(dependency.name)) {\n seen.add(dependency.name);\n pending.push(dependency.name);\n }\n }\n }\n\n const cycles = findCycles(discovered, index);\n const packages = topologicallyOrder(discovered, index);\n const chosen = new Set(selected);\n\n return {\n packages,\n added: packages.filter((name) => !chosen.has(name)),\n missing,\n versionConflicts,\n cycles,\n };\n}\n\nfunction dependenciesWithin(name: string, index: Map<string, ScenePackage>, within: Set<string>): string[] {\n const scenePackage = index.get(name);\n if (!scenePackage) return [];\n const names = scenePackage.dependencies.map((dependency) => dependency.name).filter((dependency) => within.has(dependency));\n return [...new Set(names)];\n}\n\n/**\n * Orders an already-closed set of package names so each follows the packages it depends on.\n */\nfunction topologicallyOrder(names: string[], index: Map<string, ScenePackage>): string[] {\n const within = new Set(names);\n const remaining = new Map(names.map((name) => [name, dependenciesWithin(name, index, within).length]));\n\n const ordered: string[] = [];\n const placed = new Set<string>();\n let progressed = true;\n while (progressed) {\n progressed = false;\n for (const name of names) {\n if (placed.has(name) || remaining.get(name) !== 0) continue;\n\n ordered.push(name);\n placed.add(name);\n progressed = true;\n\n for (const other of names) {\n if (placed.has(other)) continue;\n if (dependenciesWithin(other, index, within).includes(name)) {\n remaining.set(other, remaining.get(other)! - 1);\n }\n }\n }\n }\n\n // Anything left is inside a cycle, or downstream of one. It is reported through `cycles`; keeping it\n // in the list means a package picker never silently loses a package it was told about.\n ordered.push(...names.filter((name) => !placed.has(name)));\n return ordered;\n}\n\n/**\n * Finds every dependency cycle, walking depth-first from each package in discovery order so the result\n * is deterministic. A cycle is recorded once, keyed by the set of packages in it.\n */\nfunction findCycles(names: string[], index: Map<string, ScenePackage>): string[][] {\n const within = new Set(names);\n const cycles: string[][] = [];\n const recorded = new Set<string>();\n const path: string[] = [];\n const onPath = new Set<string>();\n const explored = new Set<string>();\n\n const walk = (name: string) => {\n if (onPath.has(name)) {\n const cycle = path.slice(path.indexOf(name));\n const key = [...cycle].sort().join(' ');\n if (!recorded.has(key)) {\n recorded.add(key);\n cycles.push(cycle);\n }\n\n return;\n }\n\n if (explored.has(name)) return;\n explored.add(name);\n\n path.push(name);\n onPath.add(name);\n for (const dependency of dependenciesWithin(name, index, within)) {\n walk(dependency);\n }\n\n onPath.delete(name);\n path.pop();\n };\n\n for (const name of names) {\n walk(name);\n }\n\n return cycles;\n}\n"],"names":["resolvePackageDependencies","selected","catalog","index","Map","map","scenePackage","name","discovered","seen","Set","missing","versionConflicts","pending","length","shift","push","get","dependency","dependencies","dependedOn","package","dependsOn","isVersionSatisfiedBy","version","versionRange","requiredRange","actualVersion","has","add","cycles","findCycles","packages","topologicallyOrder","chosen","added","filter","dependenciesWithin","within","names","remaining","ordered","placed","progressed","other","includes","set","recorded","path","onPath","explored","walk","cycle","slice","indexOf","key","sort","join","delete","pop"],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Layout, ScreenTemplate } from '@cratis/scene.model';
|
|
2
|
+
export interface ScreenTemplatePlacement {
|
|
3
|
+
template: string;
|
|
4
|
+
slot: string;
|
|
5
|
+
container: string;
|
|
6
|
+
depth: number;
|
|
7
|
+
}
|
|
8
|
+
export interface UnplacedScreenTemplate {
|
|
9
|
+
template: string;
|
|
10
|
+
slot: string;
|
|
11
|
+
candidates: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface ScreenTemplateResolution {
|
|
14
|
+
placements: ScreenTemplatePlacement[];
|
|
15
|
+
unplaced: UnplacedScreenTemplate[];
|
|
16
|
+
cycles: string[][];
|
|
17
|
+
}
|
|
18
|
+
export declare function isScreenTemplateResolutionValid(resolution: ScreenTemplateResolution): boolean;
|
|
19
|
+
export declare function resolveScreenTemplates(layout: Layout, templates: ScreenTemplate[]): ScreenTemplateResolution;
|
|
20
|
+
//# sourceMappingURL=resolveScreenTemplates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveScreenTemplates.d.ts","sourceRoot":"","sources":["../../resolveScreenTemplates.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAM7D,MAAM,WAAW,uBAAuB;IAEpC,QAAQ,EAAE,MAAM,CAAC;IAEjB,IAAI,EAAE,MAAM,CAAC;IAEb,SAAS,EAAE,MAAM,CAAC;IAElB,KAAK,EAAE,MAAM,CAAC;CACjB;AAMD,MAAM,WAAW,sBAAsB;IAEnC,QAAQ,EAAE,MAAM,CAAC;IAEjB,IAAI,EAAE,MAAM,CAAC;IAKb,UAAU,EAAE,MAAM,EAAE,CAAC;CACxB;AAKD,MAAM,WAAW,wBAAwB;IAErC,UAAU,EAAE,uBAAuB,EAAE,CAAC;IAMtC,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IAEnC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;CACtB;AAKD,wBAAgB,+BAA+B,CAAC,UAAU,EAAE,wBAAwB,GAAG,OAAO,CAE7F;AAcD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,wBAAwB,CA0C5G"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
/**
|
|
4
|
+
* Whether every template found exactly one home and nothing nests inside itself.
|
|
5
|
+
*/ function isScreenTemplateResolutionValid(resolution) {
|
|
6
|
+
return resolution.unplaced.length === 0 && resolution.cycles.length === 0;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Works out how a blueprint's {@link ScreenTemplate}s nest inside its {@link Layout}.
|
|
10
|
+
*
|
|
11
|
+
* A template declares only the *name* of the slot it fits, never which container owns that slot. That is
|
|
12
|
+
* what makes templates reusable — a feature's template says "I go in the module content area", not "I go
|
|
13
|
+
* inside this specific module". Resolution is the step that turns those names into a tree, by finding which
|
|
14
|
+
* layout or template declares each name.
|
|
15
|
+
*
|
|
16
|
+
* The same rule applies at every level, so nesting has no depth limit and no separate mechanism per level.
|
|
17
|
+
* The C# twin in `Cratis.Scene.Engine` implements the same algorithm; both are asserted against the same
|
|
18
|
+
* shared fixture corpus so they cannot drift apart.
|
|
19
|
+
*/ function resolveScreenTemplates(layout, templates) {
|
|
20
|
+
const containersBySlot = buildSlotIndex(layout, templates);
|
|
21
|
+
const parents = new Map();
|
|
22
|
+
const slots = new Map();
|
|
23
|
+
const unplaced = [];
|
|
24
|
+
for (const template of templates){
|
|
25
|
+
if (template.fitsSlot === undefined) continue;
|
|
26
|
+
const [qualifier, slot] = splitQualifiedSlot(template.fitsSlot);
|
|
27
|
+
const candidates = (containersBySlot.get(slot) ?? []).filter((container)=>container !== template.name && (qualifier === undefined || container === qualifier));
|
|
28
|
+
if (candidates.length === 1) {
|
|
29
|
+
parents.set(template.name, candidates[0]);
|
|
30
|
+
slots.set(template.name, slot);
|
|
31
|
+
} else {
|
|
32
|
+
unplaced.push({
|
|
33
|
+
template: template.name,
|
|
34
|
+
slot: template.fitsSlot,
|
|
35
|
+
candidates
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const cycles = findCycles(parents);
|
|
40
|
+
const inCycle = new Set(cycles.flat());
|
|
41
|
+
const placements = [];
|
|
42
|
+
for (const template of templates){
|
|
43
|
+
const container = parents.get(template.name);
|
|
44
|
+
if (container === undefined || inCycle.has(template.name)) continue;
|
|
45
|
+
placements.push({
|
|
46
|
+
template: template.name,
|
|
47
|
+
slot: slots.get(template.name),
|
|
48
|
+
container,
|
|
49
|
+
depth: depthOf(template.name, parents, layout.name)
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
placements.sort((left, right)=>left.depth - right.depth || left.template.localeCompare(right.template));
|
|
53
|
+
return {
|
|
54
|
+
placements,
|
|
55
|
+
unplaced,
|
|
56
|
+
cycles
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Splits a `fitsSlot` into the container it names and the slot within it.
|
|
61
|
+
*
|
|
62
|
+
* The same rule component names use: a bare name searches, a qualified one goes straight to what it names.
|
|
63
|
+
* A slot called `body` is a good name at every level of a nesting chain, so several templates legitimately
|
|
64
|
+
* declare one — and a bare `body` then has no single answer. Qualifying it says which, without forcing
|
|
65
|
+
* every slot in an application to carry a unique name.
|
|
66
|
+
*/ function splitQualifiedSlot(fitsSlot) {
|
|
67
|
+
const lastDot = fitsSlot.lastIndexOf('.');
|
|
68
|
+
return lastDot < 0 ? [
|
|
69
|
+
undefined,
|
|
70
|
+
fitsSlot
|
|
71
|
+
] : [
|
|
72
|
+
fitsSlot.substring(0, lastDot),
|
|
73
|
+
fitsSlot.substring(lastDot + 1)
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
function buildSlotIndex(layout, templates) {
|
|
77
|
+
const index = new Map();
|
|
78
|
+
const declare = (slot, container)=>{
|
|
79
|
+
const containers = index.get(slot) ?? [];
|
|
80
|
+
if (!containers.includes(container)) containers.push(container);
|
|
81
|
+
index.set(slot, containers);
|
|
82
|
+
};
|
|
83
|
+
for (const slot of layout.slots)declare(slot.name, layout.name);
|
|
84
|
+
for (const template of templates){
|
|
85
|
+
for (const slot of template.slots)declare(slot.name, template.name);
|
|
86
|
+
}
|
|
87
|
+
return index;
|
|
88
|
+
}
|
|
89
|
+
function findCycles(parents) {
|
|
90
|
+
const cycles = [];
|
|
91
|
+
const recorded = new Set();
|
|
92
|
+
for (const start of [
|
|
93
|
+
...parents.keys()
|
|
94
|
+
].sort()){
|
|
95
|
+
const path = [];
|
|
96
|
+
const onPath = new Set();
|
|
97
|
+
let current = start;
|
|
98
|
+
for(;;){
|
|
99
|
+
const parent = parents.get(current);
|
|
100
|
+
if (parent === undefined) break;
|
|
101
|
+
path.push(current);
|
|
102
|
+
onPath.add(current);
|
|
103
|
+
if (onPath.has(parent)) {
|
|
104
|
+
const cycle = path.slice(path.indexOf(parent));
|
|
105
|
+
const key = [
|
|
106
|
+
...cycle
|
|
107
|
+
].sort().join(' ');
|
|
108
|
+
if (!recorded.has(key)) {
|
|
109
|
+
recorded.add(key);
|
|
110
|
+
cycles.push(cycle);
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
onPath.add(parent);
|
|
115
|
+
current = parent;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return cycles;
|
|
119
|
+
}
|
|
120
|
+
function depthOf(template, parents, layoutName) {
|
|
121
|
+
let depth = 0;
|
|
122
|
+
let current = template;
|
|
123
|
+
while(parents.has(current) && depth <= parents.size){
|
|
124
|
+
depth++;
|
|
125
|
+
const parent = parents.get(current);
|
|
126
|
+
if (parent === layoutName) break;
|
|
127
|
+
current = parent;
|
|
128
|
+
}
|
|
129
|
+
return depth;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export { isScreenTemplateResolutionValid, resolveScreenTemplates };
|
|
133
|
+
//# sourceMappingURL=resolveScreenTemplates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveScreenTemplates.js","sources":["../../resolveScreenTemplates.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { Layout, ScreenTemplate } from '@cratis/scene.model';\n\n/**\n * Where one {@link ScreenTemplate} ended up: the slot it declared, and the layout or template that turned\n * out to declare that slot.\n */\nexport interface ScreenTemplatePlacement {\n /** The template being placed. */\n template: string;\n /** The slot name the template declared it fits. */\n slot: string;\n /** The name of the {@link Layout} or {@link ScreenTemplate} declaring that slot. */\n container: string;\n /** How far below the layout the template sits — 1 directly inside the layout, 2 inside a template that is. */\n depth: number;\n}\n\n/**\n * A template that names a slot, where the set of containers in scope does not agree on exactly one home for\n * it.\n */\nexport interface UnplacedScreenTemplate {\n /** The template that could not be placed. */\n template: string;\n /** The slot name it declared it fits. */\n slot: string;\n /**\n * The containers declaring a slot of that name. Empty when nothing declares it; more than one when the\n * name is ambiguous. Never exactly one — that case is a {@link ScreenTemplatePlacement} instead.\n */\n candidates: string[];\n}\n\n/**\n * The outcome of working out how a blueprint's templates nest inside its layout.\n */\nexport interface ScreenTemplateResolution {\n /** Every template that found exactly one home, ordered shallowest first so a caller can build the tree top-down. */\n placements: ScreenTemplatePlacement[];\n /**\n * Templates that found none or several. Reported rather than guessed: placing a template in the wrong\n * parent renders content in the wrong region, which is far harder to diagnose than being told the slot\n * name is ambiguous.\n */\n unplaced: UnplacedScreenTemplate[];\n /** Template nesting cycles — a template that transitively contains itself. */\n cycles: string[][];\n}\n\n/**\n * Whether every template found exactly one home and nothing nests inside itself.\n */\nexport function isScreenTemplateResolutionValid(resolution: ScreenTemplateResolution): boolean {\n return resolution.unplaced.length === 0 && resolution.cycles.length === 0;\n}\n\n/**\n * Works out how a blueprint's {@link ScreenTemplate}s nest inside its {@link Layout}.\n *\n * A template declares only the *name* of the slot it fits, never which container owns that slot. That is\n * what makes templates reusable — a feature's template says \"I go in the module content area\", not \"I go\n * inside this specific module\". Resolution is the step that turns those names into a tree, by finding which\n * layout or template declares each name.\n *\n * The same rule applies at every level, so nesting has no depth limit and no separate mechanism per level.\n * The C# twin in `Cratis.Scene.Engine` implements the same algorithm; both are asserted against the same\n * shared fixture corpus so they cannot drift apart.\n */\nexport function resolveScreenTemplates(layout: Layout, templates: ScreenTemplate[]): ScreenTemplateResolution {\n const containersBySlot = buildSlotIndex(layout, templates);\n const parents = new Map<string, string>();\n const slots = new Map<string, string>();\n const unplaced: UnplacedScreenTemplate[] = [];\n\n for (const template of templates) {\n if (template.fitsSlot === undefined) continue;\n\n const [qualifier, slot] = splitQualifiedSlot(template.fitsSlot);\n\n const candidates = (containersBySlot.get(slot) ?? []).filter(\n (container) => container !== template.name && (qualifier === undefined || container === qualifier)\n );\n\n if (candidates.length === 1) {\n parents.set(template.name, candidates[0]);\n slots.set(template.name, slot);\n } else {\n unplaced.push({ template: template.name, slot: template.fitsSlot, candidates });\n }\n }\n\n const cycles = findCycles(parents);\n const inCycle = new Set(cycles.flat());\n\n const placements: ScreenTemplatePlacement[] = [];\n for (const template of templates) {\n const container = parents.get(template.name);\n if (container === undefined || inCycle.has(template.name)) continue;\n\n placements.push({\n template: template.name,\n slot: slots.get(template.name)!,\n container,\n depth: depthOf(template.name, parents, layout.name),\n });\n }\n\n placements.sort((left, right) => left.depth - right.depth || left.template.localeCompare(right.template));\n\n return { placements, unplaced, cycles };\n}\n\n/**\n * Splits a `fitsSlot` into the container it names and the slot within it.\n *\n * The same rule component names use: a bare name searches, a qualified one goes straight to what it names.\n * A slot called `body` is a good name at every level of a nesting chain, so several templates legitimately\n * declare one — and a bare `body` then has no single answer. Qualifying it says which, without forcing\n * every slot in an application to carry a unique name.\n */\nfunction splitQualifiedSlot(fitsSlot: string): [string | undefined, string] {\n const lastDot = fitsSlot.lastIndexOf('.');\n return lastDot < 0 ? [undefined, fitsSlot] : [fitsSlot.substring(0, lastDot), fitsSlot.substring(lastDot + 1)];\n}\n\nfunction buildSlotIndex(layout: Layout, templates: ScreenTemplate[]): Map<string, string[]> {\n const index = new Map<string, string[]>();\n\n const declare = (slot: string, container: string) => {\n const containers = index.get(slot) ?? [];\n if (!containers.includes(container)) containers.push(container);\n index.set(slot, containers);\n };\n\n for (const slot of layout.slots) declare(slot.name, layout.name);\n for (const template of templates) {\n for (const slot of template.slots) declare(slot.name, template.name);\n }\n\n return index;\n}\n\nfunction findCycles(parents: Map<string, string>): string[][] {\n const cycles: string[][] = [];\n const recorded = new Set<string>();\n\n for (const start of [...parents.keys()].sort()) {\n const path: string[] = [];\n const onPath = new Set<string>();\n let current = start;\n\n for (;;) {\n const parent = parents.get(current);\n if (parent === undefined) break;\n\n path.push(current);\n onPath.add(current);\n if (onPath.has(parent)) {\n const cycle = path.slice(path.indexOf(parent));\n const key = [...cycle].sort().join(' ');\n if (!recorded.has(key)) {\n recorded.add(key);\n cycles.push(cycle);\n }\n\n break;\n }\n\n onPath.add(parent);\n current = parent;\n }\n }\n\n return cycles;\n}\n\nfunction depthOf(template: string, parents: Map<string, string>, layoutName: string): number {\n let depth = 0;\n let current = template;\n while (parents.has(current) && depth <= parents.size) {\n depth++;\n const parent = parents.get(current)!;\n if (parent === layoutName) break;\n current = parent;\n }\n\n return depth;\n}\n"],"names":["isScreenTemplateResolutionValid","resolution","unplaced","length","cycles","resolveScreenTemplates","layout","templates","containersBySlot","buildSlotIndex","parents","Map","slots","template","fitsSlot","undefined","qualifier","slot","splitQualifiedSlot","candidates","get","filter","container","name","set","push","findCycles","inCycle","Set","flat","placements","has","depth","depthOf","sort","left","right","localeCompare","lastDot","lastIndexOf","substring","index","declare","containers","includes","recorded","start","keys","path","onPath","current","parent","add","cycle","slice","indexOf","key","join","layoutName","size"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Theme, UiProfile } from '@cratis/scene.model';
|
|
2
|
+
export declare function incompatiblePackages(theme: Theme, profile: UiProfile): string[];
|
|
3
|
+
export declare function isThemeCompatible(theme: Theme, profile: UiProfile): boolean;
|
|
4
|
+
export declare function applicableThemePackages(theme: Theme, profile: UiProfile): string[];
|
|
5
|
+
//# sourceMappingURL=themeCompatibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themeCompatibility.d.ts","sourceRoot":"","sources":["../../themeCompatibility.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAevD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,EAAE,CAE/E;AASD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO,CAE3E;AAYD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,EAAE,CAElF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
/**
|
|
4
|
+
* Finds the packages a {@link UiProfile} declares that a {@link Theme} is not declared compatible with -
|
|
5
|
+
* part of Cratis/Scene#5, the same rule Screenplay's compiler already applies to warn on an incompatible
|
|
6
|
+
* `ui profile`/`theme` pairing, reused here so `Scene.React` knows which packages to actually apply a
|
|
7
|
+
* theme's tokens to, and so Studio's design-time tooling and Stage's build-time checks don't each
|
|
8
|
+
* reimplement it. There is deliberately no implicit exemption for `core`: a theme wanting broad
|
|
9
|
+
* applicability declares `compatible with core` itself, exactly like the profile/package resolver has no
|
|
10
|
+
* implicit special case for it either.
|
|
11
|
+
*
|
|
12
|
+
* @param theme The {@link Theme} to check.
|
|
13
|
+
* @param profile The {@link UiProfile} whose packages to check against.
|
|
14
|
+
* @returns The packages in `profile.packages` that `theme` does not declare compatibility with.
|
|
15
|
+
*/ function incompatiblePackages(theme, profile) {
|
|
16
|
+
return profile.packages.filter((package_)=>!theme.compatibleWith.includes(package_));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Whether a {@link Theme} is compatible with every package a {@link UiProfile} declares.
|
|
20
|
+
*
|
|
21
|
+
* @param theme The {@link Theme} to check.
|
|
22
|
+
* @param profile The {@link UiProfile} whose packages to check against.
|
|
23
|
+
* @returns `true` when `theme` declares compatibility with every package `profile` lists.
|
|
24
|
+
*/ function isThemeCompatible(theme, profile) {
|
|
25
|
+
return incompatiblePackages(theme, profile).length === 0;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Finds the packages a {@link Theme}'s tokens actually apply to for a given {@link UiProfile} - the
|
|
29
|
+
* packages the profile activates that the theme also declares compatibility with. This is what a
|
|
30
|
+
* renderer scopes token application to, rather than applying a theme's tokens globally - and is
|
|
31
|
+
* recomputed on every theme switch for the live re-resolution Cratis/Scene#5 requires (no reload).
|
|
32
|
+
*
|
|
33
|
+
* @param theme The {@link Theme} to apply.
|
|
34
|
+
* @param profile The active {@link UiProfile}.
|
|
35
|
+
* @returns The packages both active in `profile` and declared compatible by `theme`.
|
|
36
|
+
*/ function applicableThemePackages(theme, profile) {
|
|
37
|
+
return profile.packages.filter((package_)=>theme.compatibleWith.includes(package_));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { applicableThemePackages, incompatiblePackages, isThemeCompatible };
|
|
41
|
+
//# sourceMappingURL=themeCompatibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themeCompatibility.js","sources":["../../themeCompatibility.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { Theme, UiProfile } from '@cratis/scene.model';\n\n/**\n * Finds the packages a {@link UiProfile} declares that a {@link Theme} is not declared compatible with -\n * part of Cratis/Scene#5, the same rule Screenplay's compiler already applies to warn on an incompatible\n * `ui profile`/`theme` pairing, reused here so `Scene.React` knows which packages to actually apply a\n * theme's tokens to, and so Studio's design-time tooling and Stage's build-time checks don't each\n * reimplement it. There is deliberately no implicit exemption for `core`: a theme wanting broad\n * applicability declares `compatible with core` itself, exactly like the profile/package resolver has no\n * implicit special case for it either.\n *\n * @param theme The {@link Theme} to check.\n * @param profile The {@link UiProfile} whose packages to check against.\n * @returns The packages in `profile.packages` that `theme` does not declare compatibility with.\n */\nexport function incompatiblePackages(theme: Theme, profile: UiProfile): string[] {\n return profile.packages.filter(package_ => !theme.compatibleWith.includes(package_));\n}\n\n/**\n * Whether a {@link Theme} is compatible with every package a {@link UiProfile} declares.\n *\n * @param theme The {@link Theme} to check.\n * @param profile The {@link UiProfile} whose packages to check against.\n * @returns `true` when `theme` declares compatibility with every package `profile` lists.\n */\nexport function isThemeCompatible(theme: Theme, profile: UiProfile): boolean {\n return incompatiblePackages(theme, profile).length === 0;\n}\n\n/**\n * Finds the packages a {@link Theme}'s tokens actually apply to for a given {@link UiProfile} - the\n * packages the profile activates that the theme also declares compatibility with. This is what a\n * renderer scopes token application to, rather than applying a theme's tokens globally - and is\n * recomputed on every theme switch for the live re-resolution Cratis/Scene#5 requires (no reload).\n *\n * @param theme The {@link Theme} to apply.\n * @param profile The active {@link UiProfile}.\n * @returns The packages both active in `profile` and declared compatible by `theme`.\n */\nexport function applicableThemePackages(theme: Theme, profile: UiProfile): string[] {\n return profile.packages.filter(package_ => theme.compatibleWith.includes(package_));\n}\n"],"names":["incompatiblePackages","theme","profile","packages","filter","package_","compatibleWith","includes","isThemeCompatible","length","applicableThemePackages"],"mappings":"AAAA;AACA;AAIA;;;;;;;;;;;;AAYC,IACM,SAASA,oBAAAA,CAAqBC,KAAY,EAAEC,OAAkB,EAAA;AACjE,IAAA,OAAOA,OAAAA,CAAQC,QAAQ,CAACC,MAAM,CAACC,CAAAA,QAAAA,GAAY,CAACJ,KAAAA,CAAMK,cAAc,CAACC,QAAQ,CAACF,QAAAA,CAAAA,CAAAA;AAC9E;AAEA;;;;;;AAMC,IACM,SAASG,iBAAAA,CAAkBP,KAAY,EAAEC,OAAkB,EAAA;AAC9D,IAAA,OAAOF,oBAAAA,CAAqBC,KAAAA,EAAOC,OAAAA,CAAAA,CAASO,MAAM,KAAK,CAAA;AAC3D;AAEA;;;;;;;;;AASC,IACM,SAASC,uBAAAA,CAAwBT,KAAY,EAAEC,OAAkB,EAAA;IACpE,OAAOA,OAAAA,CAAQC,QAAQ,CAACC,MAAM,CAACC,CAAAA,QAAAA,GAAYJ,KAAAA,CAAMK,cAAc,CAACC,QAAQ,CAACF,QAAAA,CAAAA,CAAAA;AAC7E;;;;"}
|