Skip to content

Commit f539ca1

Browse files
committed
add provideAsync
1 parent 009ffd8 commit f539ca1

2 files changed

Lines changed: 57 additions & 5 deletions

File tree

src/providable.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ const [provide, getProvide, initProvide] = createMark<CustomElement>(
4545
}
4646
}
4747
)
48+
const [provideAsync, getProvideAsync, initProvideAsync] = createMark<CustomElement>(
49+
({name, kind}) => {
50+
if (kind === 'setter') throw new Error(`@provide cannot decorate setter ${String(name)}`)
51+
if (kind === 'method') throw new Error(`@provide cannot decorate method ${String(name)}`)
52+
},
53+
(instance: CustomElement, {name, kind, access}) => {
54+
return {
55+
get: () => (kind === 'getter' ? access.get!.call(instance) : access.value),
56+
set: (newValue: unknown) => {
57+
access.set?.call(instance, newValue)
58+
for (const callback of contexts.get(instance)?.get(name) || []) callback(newValue)
59+
}
60+
}
61+
}
62+
)
4863
const [consume, getConsume, initConsume] = createMark<CustomElement>(
4964
({name, kind}) => {
5065
if (kind === 'method') throw new Error(`@consume cannot decorate method ${String(name)}`)
@@ -75,7 +90,7 @@ const [consume, getConsume, initConsume] = createMark<CustomElement>(
7590

7691
const disposes = new WeakMap<CustomElement, Map<PropertyKey, () => void>>()
7792

78-
export {consume, provide, getProvide, getConsume}
93+
export {consume, provide, provideAsync, getProvide, getProvideAsync, getConsume}
7994
export const providable = createAbility(
8095
<T extends CustomElementClass>(Class: T): T =>
8196
class extends Class {
@@ -86,18 +101,23 @@ export const providable = createAbility(
86101
constructor(...args: any[]) {
87102
super(...args)
88103
initProvide(this)
104+
initProvideAsync(this)
89105
const provides = getProvide(this)
90-
if (provides.size) {
106+
const providesAsync = getProvideAsync(this)
107+
if (provides.size || providesAsync.size) {
91108
if (!contexts.has(this)) contexts.set(this, new Map())
92109
const instanceContexts = contexts.get(this)!
93110
this.addEventListener('context-request', event => {
94111
if (!isContextEvent(event)) return
95112
const name = event.context.name
96-
if (!provides.has(name)) return
113+
if (!provides.has(name) && !providesAsync.has(name)) return
97114
const value = this[name]
98115
const dispose = () => instanceContexts.get(name)?.delete(callback)
99116
const eventCallback = event.callback
100-
const callback = (newValue: unknown) => eventCallback(newValue, dispose)
117+
let callback = (newValue: unknown) => eventCallback(newValue, dispose)
118+
if (providesAsync.has(name)) {
119+
callback = async (newValue: unknown) => eventCallback(await newValue, dispose)
120+
}
101121
if (event.multiple) {
102122
if (!instanceContexts.has(name)) instanceContexts.set(name, new Set())
103123
instanceContexts.get(name)!.add(callback)

test/providable.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {expect, fixture, html} from '@open-wc/testing'
22
import {fake} from 'sinon'
3-
import {provide, consume, providable, ContextEvent} from '../src/providable.js'
3+
import {provide, provideAsync, consume, providable, ContextEvent} from '../src/providable.js'
44

55
describe('Providable', () => {
66
const sym = Symbol('bing')
@@ -16,6 +16,18 @@ describe('Providable', () => {
1616
}
1717
window.customElements.define('providable-provider-test', ProvidableProviderTest)
1818

19+
@providable
20+
class AsyncProvidableProviderTest extends HTMLElement {
21+
@provideAsync foo = Promise.resolve('hello')
22+
@provideAsync bar = Promise.resolve('world')
23+
@provideAsync get baz() {
24+
return Promise.resolve(3)
25+
}
26+
@provideAsync [sym] = Promise.resolve({provided: true})
27+
@provideAsync qux = Promise.resolve(8)
28+
}
29+
window.customElements.define('async-providable-provider-test', AsyncProvidableProviderTest)
30+
1931
@providable
2032
class ProvidableSomeProviderTest extends HTMLElement {
2133
@provide foo = 'greetings'
@@ -277,6 +289,26 @@ describe('Providable', () => {
277289
})
278290
})
279291

292+
describe('async provider', () => {
293+
let provider: AsyncProvidableProviderTest
294+
let consumer: ProvidableConsumerTest
295+
beforeEach(async () => {
296+
provider = await fixture(html`<async-providable-provider-test>
297+
<providable-consumer-test></providable-consumer-test>
298+
</async-providable-provider-test>`)
299+
consumer = provider.querySelector<ProvidableConsumerTest>('providable-consumer-test')!
300+
})
301+
302+
it('passes resovled values to consumer', async () => {
303+
expect(consumer).to.have.property('foo', 'hello')
304+
expect(consumer).to.have.property('bar', 'world')
305+
expect(consumer).to.have.property('baz', 3)
306+
expect(consumer).to.have.property(sym).eql({provided: true})
307+
expect(consumer).to.have.property('qux').eql(8)
308+
expect(consumer).to.have.property('count').eql(1)
309+
})
310+
})
311+
280312
describe('error scenarios', () => {
281313
it('cannot decorate methods as providers', () => {
282314
expect(() => {

0 commit comments

Comments
 (0)