assign-gingerly 0.0.49 → 0.0.51
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 +1 -0
- package/defineWithFeatures.js +5 -1
- package/defineWithFeatures.ts +15 -1
- package/index.js +1 -0
- package/index.ts +1 -0
- package/package.json +5 -1
- package/resolveAndAssignFeatures.js +67 -0
- package/resolveAndAssignFeatures.ts +79 -0
package/README.md
CHANGED
|
@@ -4855,6 +4855,7 @@ For full documentation, see [docs/defineWithFeatures.md](docs/defineWithFeatures
|
|
|
4855
4855
|
| [face-up](https://www.npmjs.com/package/face-up) | Form Associated Custom Element behavior via ElementInternals | [GitHub](https://github.com/bahrus/face-up) |
|
|
4856
4856
|
| [roundabout](https://www.npmjs.com/package/roundabout) | Reactive view-model binding with template rendering and computed property orchestration | [GitHub](https://github.com/bahrus/roundabout#using-roundaboutfeature-with-assignfeatures) |
|
|
4857
4857
|
| [time-ticker](https://www.npmjs.com/package/time-ticker) | Web component that fires events periodically (example of a feature-based component with no code in the class) | [GitHub](https://github.com/bahrus/time-ticker) |
|
|
4858
|
+
| [templ-maker](https://www.npmjs.com/package/templ-maker) | Extracts a DOM fragment into a reusable template and clones it per instance (works with cede scripts) | [GitHub](https://github.com/bahrus/templ-maker) |
|
|
4858
4859
|
|
|
4859
4860
|
</details>
|
|
4860
4861
|
|
package/defineWithFeatures.js
CHANGED
|
@@ -52,7 +52,7 @@ const resolvedSpawnCache = new WeakMap();
|
|
|
52
52
|
* @param registry - Optional custom element registry (defaults to global `customElements`)
|
|
53
53
|
* @returns The newly created and defined custom element class
|
|
54
54
|
*/
|
|
55
|
-
export async function defineWithFeatures(tagName, baseTagName, config, registry) {
|
|
55
|
+
export async function defineWithFeatures(tagName, baseTagName, config, registry, options) {
|
|
56
56
|
const reg = registry || customElements;
|
|
57
57
|
// 1. Resolve base class — wait for it if not yet defined
|
|
58
58
|
let BaseClass = reg.get(baseTagName);
|
|
@@ -99,6 +99,10 @@ export async function defineWithFeatures(tagName, baseTagName, config, registry)
|
|
|
99
99
|
// 3. Create subclass
|
|
100
100
|
const NewClass = class extends BaseClass {
|
|
101
101
|
};
|
|
102
|
+
// 3b. Call onSubclassCreated callback (before define, before features if needed)
|
|
103
|
+
if (options?.onSubclassCreated) {
|
|
104
|
+
options.onSubclassCreated(NewClass);
|
|
105
|
+
}
|
|
102
106
|
// 4. Build FeatureConfigsMap: resolved spawns + JSON config
|
|
103
107
|
const featuresMap = {};
|
|
104
108
|
for (const [key, jsonConfig] of Object.entries(config.assignFeatures)) {
|
package/defineWithFeatures.ts
CHANGED
|
@@ -33,6 +33,14 @@ export interface DefineWithFeaturesConfig {
|
|
|
33
33
|
}>;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Options for defineWithFeatures.
|
|
38
|
+
*/
|
|
39
|
+
export interface DefineWithFeaturesOptions {
|
|
40
|
+
/** Called after the subclass is created but before registry.define(). */
|
|
41
|
+
onSubclassCreated?: (NewCtr: Function) => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
/**
|
|
37
45
|
* Determines if a function is an async spawner (same heuristic as assignFeatures).
|
|
38
46
|
*/
|
|
@@ -68,7 +76,8 @@ export async function defineWithFeatures(
|
|
|
68
76
|
tagName: string,
|
|
69
77
|
baseTagName: string,
|
|
70
78
|
config: DefineWithFeaturesConfig,
|
|
71
|
-
registry?: CustomElementRegistry
|
|
79
|
+
registry?: CustomElementRegistry,
|
|
80
|
+
options?: DefineWithFeaturesOptions
|
|
72
81
|
): Promise<Function> {
|
|
73
82
|
const reg = registry || customElements;
|
|
74
83
|
|
|
@@ -129,6 +138,11 @@ export async function defineWithFeatures(
|
|
|
129
138
|
// 3. Create subclass
|
|
130
139
|
const NewClass = class extends (BaseClass as any) {};
|
|
131
140
|
|
|
141
|
+
// 3b. Call onSubclassCreated callback (before define, before features if needed)
|
|
142
|
+
if (options?.onSubclassCreated) {
|
|
143
|
+
options.onSubclassCreated(NewClass);
|
|
144
|
+
}
|
|
145
|
+
|
|
132
146
|
// 4. Build FeatureConfigsMap: resolved spawns + JSON config
|
|
133
147
|
const featuresMap: FeatureConfigsMap = {};
|
|
134
148
|
for (const [key, jsonConfig] of Object.entries(config.assignFeatures)) {
|
package/index.js
CHANGED
|
@@ -12,4 +12,5 @@ export { assignFrom } from './assignFrom.js';
|
|
|
12
12
|
export { assignFeatures, FeaturesRegistry, captureFeatureInitVals, PropertyBag, suggestFeatureInfo, getFeatureInfoSuggestions } from './assignFeatures.js';
|
|
13
13
|
export { installForwarding } from './installForwarding.js';
|
|
14
14
|
export { defineWithFeatures } from './defineWithFeatures.js';
|
|
15
|
+
export { resolveAndAssignFeatures } from './resolveAndAssignFeatures.js';
|
|
15
16
|
import './object-extension.js';
|
package/index.ts
CHANGED
|
@@ -12,4 +12,5 @@ export {assignFrom} from './assignFrom.js';
|
|
|
12
12
|
export {assignFeatures, FeaturesRegistry, captureFeatureInitVals, PropertyBag, suggestFeatureInfo, getFeatureInfoSuggestions} from './assignFeatures.js';
|
|
13
13
|
export {installForwarding} from './installForwarding.js';
|
|
14
14
|
export {defineWithFeatures} from './defineWithFeatures.js';
|
|
15
|
+
export {resolveAndAssignFeatures} from './resolveAndAssignFeatures.js';
|
|
15
16
|
import './object-extension.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assign-gingerly",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.51",
|
|
4
4
|
"description": "This package provides a utility function for carefully merging one object into another.",
|
|
5
5
|
"homepage": "https://github.com/bahrus/assign-gingerly#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -68,6 +68,10 @@
|
|
|
68
68
|
"default": "./defineWithFeatures.js",
|
|
69
69
|
"types": "./defineWithFeatures.ts"
|
|
70
70
|
},
|
|
71
|
+
"./resolveAndAssignFeatures.js": {
|
|
72
|
+
"default": "./resolveAndAssignFeatures.js",
|
|
73
|
+
"types": "./resolveAndAssignFeatures.ts"
|
|
74
|
+
},
|
|
71
75
|
"./assignFrom.js": {
|
|
72
76
|
"default": "./assignFrom.js",
|
|
73
77
|
"types": "./assignFrom.ts"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolveAndAssignFeatures - Resolves async fallback spawns then calls assignFeatures.
|
|
3
|
+
*
|
|
4
|
+
* For each feature in the config that doesn't have an explicit `spawn`, resolves
|
|
5
|
+
* the async `fallbackSpawn` from the class's `static supportedFeatures` and sets
|
|
6
|
+
* it as the spawn. Then calls `assignFeatures` with the fully resolved config.
|
|
7
|
+
*
|
|
8
|
+
* This eliminates the boilerplate of manually resolving async spawns before
|
|
9
|
+
* calling assignFeatures.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* import { resolveAndAssignFeatures } from 'assign-gingerly/resolveAndAssignFeatures.js';
|
|
13
|
+
*
|
|
14
|
+
* await resolveAndAssignFeatures(MyElement, {
|
|
15
|
+
* roundabout: {
|
|
16
|
+
* customData: {...},
|
|
17
|
+
* withAttrs: {...},
|
|
18
|
+
* callbackForwarding: ['connectedCallback']
|
|
19
|
+
* },
|
|
20
|
+
* faceUp: {
|
|
21
|
+
* callbackForwarding: ['connectedCallback', 'disconnectedCallback']
|
|
22
|
+
* }
|
|
23
|
+
* });
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Determines if a function is an async spawner.
|
|
27
|
+
*/
|
|
28
|
+
function isAsyncSpawn(fn) {
|
|
29
|
+
if (typeof fn !== 'function')
|
|
30
|
+
return false;
|
|
31
|
+
if (fn.constructor.name === 'AsyncFunction')
|
|
32
|
+
return true;
|
|
33
|
+
if (fn.prototype === undefined)
|
|
34
|
+
return true;
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolves async fallback spawns for features that don't have an explicit spawn,
|
|
39
|
+
* then calls assignFeatures on the registry.
|
|
40
|
+
*
|
|
41
|
+
* @param ElementClass - The custom element class (must have static supportedFeatures)
|
|
42
|
+
* @param featuresConfig - Feature configurations (spawn will be resolved from fallbackSpawn if missing)
|
|
43
|
+
* @param registry - Optional CustomElementRegistry (defaults to global customElements)
|
|
44
|
+
*/
|
|
45
|
+
export async function resolveAndAssignFeatures(ElementClass, featuresConfig, registry) {
|
|
46
|
+
const supportedFeatures = ElementClass.supportedFeatures;
|
|
47
|
+
if (!supportedFeatures) {
|
|
48
|
+
throw new Error(`resolveAndAssignFeatures: ${ElementClass.name || 'constructor'} does not define static supportedFeatures`);
|
|
49
|
+
}
|
|
50
|
+
// Resolve async fallback spawns in parallel for features without explicit spawn
|
|
51
|
+
await Promise.all(Object.entries(featuresConfig).map(async ([key, featureConfig]) => {
|
|
52
|
+
// Skip if spawn is already provided
|
|
53
|
+
if (featureConfig.spawn)
|
|
54
|
+
return;
|
|
55
|
+
const optIn = supportedFeatures[key];
|
|
56
|
+
if (!optIn?.fallbackSpawn)
|
|
57
|
+
return;
|
|
58
|
+
let spawn = optIn.fallbackSpawn;
|
|
59
|
+
if (isAsyncSpawn(spawn)) {
|
|
60
|
+
spawn = await spawn();
|
|
61
|
+
}
|
|
62
|
+
featureConfig.spawn = spawn;
|
|
63
|
+
}));
|
|
64
|
+
// Call assignFeatures on the registry
|
|
65
|
+
const reg = registry || customElements;
|
|
66
|
+
await reg.assignFeatures(ElementClass, featuresConfig);
|
|
67
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolveAndAssignFeatures - Resolves async fallback spawns then calls assignFeatures.
|
|
3
|
+
*
|
|
4
|
+
* For each feature in the config that doesn't have an explicit `spawn`, resolves
|
|
5
|
+
* the async `fallbackSpawn` from the class's `static supportedFeatures` and sets
|
|
6
|
+
* it as the spawn. Then calls `assignFeatures` with the fully resolved config.
|
|
7
|
+
*
|
|
8
|
+
* This eliminates the boilerplate of manually resolving async spawns before
|
|
9
|
+
* calling assignFeatures.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* import { resolveAndAssignFeatures } from 'assign-gingerly/resolveAndAssignFeatures.js';
|
|
13
|
+
*
|
|
14
|
+
* await resolveAndAssignFeatures(MyElement, {
|
|
15
|
+
* roundabout: {
|
|
16
|
+
* customData: {...},
|
|
17
|
+
* withAttrs: {...},
|
|
18
|
+
* callbackForwarding: ['connectedCallback']
|
|
19
|
+
* },
|
|
20
|
+
* faceUp: {
|
|
21
|
+
* callbackForwarding: ['connectedCallback', 'disconnectedCallback']
|
|
22
|
+
* }
|
|
23
|
+
* });
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { FeatureConfigsMap, SupportedFeaturesMap } from './types/assign-gingerly/types.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Determines if a function is an async spawner.
|
|
30
|
+
*/
|
|
31
|
+
function isAsyncSpawn(fn: any): boolean {
|
|
32
|
+
if (typeof fn !== 'function') return false;
|
|
33
|
+
if (fn.constructor.name === 'AsyncFunction') return true;
|
|
34
|
+
if (fn.prototype === undefined) return true;
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolves async fallback spawns for features that don't have an explicit spawn,
|
|
40
|
+
* then calls assignFeatures on the registry.
|
|
41
|
+
*
|
|
42
|
+
* @param ElementClass - The custom element class (must have static supportedFeatures)
|
|
43
|
+
* @param featuresConfig - Feature configurations (spawn will be resolved from fallbackSpawn if missing)
|
|
44
|
+
* @param registry - Optional CustomElementRegistry (defaults to global customElements)
|
|
45
|
+
*/
|
|
46
|
+
export async function resolveAndAssignFeatures(
|
|
47
|
+
ElementClass: Function,
|
|
48
|
+
featuresConfig: FeatureConfigsMap,
|
|
49
|
+
registry?: any
|
|
50
|
+
): Promise<void> {
|
|
51
|
+
const supportedFeatures: SupportedFeaturesMap | undefined = (ElementClass as any).supportedFeatures;
|
|
52
|
+
|
|
53
|
+
if (!supportedFeatures) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`resolveAndAssignFeatures: ${(ElementClass as any).name || 'constructor'} does not define static supportedFeatures`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Resolve async fallback spawns in parallel for features without explicit spawn
|
|
60
|
+
await Promise.all(
|
|
61
|
+
Object.entries(featuresConfig).map(async ([key, featureConfig]) => {
|
|
62
|
+
// Skip if spawn is already provided
|
|
63
|
+
if (featureConfig.spawn) return;
|
|
64
|
+
|
|
65
|
+
const optIn = supportedFeatures[key];
|
|
66
|
+
if (!optIn?.fallbackSpawn) return;
|
|
67
|
+
|
|
68
|
+
let spawn = optIn.fallbackSpawn;
|
|
69
|
+
if (isAsyncSpawn(spawn)) {
|
|
70
|
+
spawn = await (spawn as () => Promise<any>)();
|
|
71
|
+
}
|
|
72
|
+
(featureConfig as any).spawn = spawn;
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Call assignFeatures on the registry
|
|
77
|
+
const reg = registry || customElements;
|
|
78
|
+
await reg.assignFeatures(ElementClass, featuresConfig);
|
|
79
|
+
}
|