Skip to content

Commit

Permalink
fix(di): shift in loop data
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzofox3 committed Mar 20, 2024
1 parent 36c0d22 commit ecc14fa
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 10 deletions.
2 changes: 1 addition & 1 deletion packages/di/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cofn/di",
"version": "0.0.1",
"version": "0.0.2",
"description": "DOM as DI container",
"type": "module",
"exports": {
Expand Down
30 changes: 23 additions & 7 deletions packages/di/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const registrySymbol = Symbol('registry');
export const provide = (providerFn) => (comp) => {
const _providerFn = factorify(providerFn);
return function* ({ $host, ...rest }) {
yield; // The element must be mounted, so we can look up the DOM tree
let input = yield; // The element must be mounted, so we can look up the DOM tree
$host[registrySymbol] = Object.assign(
Object.create(
$host.closest('[provider]')?.[registrySymbol] ?? rootRegistry,
Expand All @@ -16,22 +16,38 @@ export const provide = (providerFn) => (comp) => {

const instance = comp({
$host,
services: createInjector({
services: $host[registrySymbol],
}),
...rest,
});

instance.next();

yield* instance;
instance.next(); // need to catch up as we defer instantiation
try {
while (true) {
instance.next(input);
input = yield;
}
} finally {
instance.return();
}
};
};

export const inject = (comp) =>
function* ({ $host, ...rest }) {
yield; // The element must be mounted, so we can look up the DOM tree
let input = yield; // The element must be mounted, so we can look up the DOM tree
const services = createInjector({
services: $host.closest('[provider]')?.[registrySymbol] ?? rootRegistry,
});
const instance = comp({ $host, services, ...rest });
instance.next();
yield* instance;
try {
instance.next(); // need to catch up as we defer instantiation
while (true) {
instance.next(input);
input = yield;
}
} finally {
instance.return();
}
};
90 changes: 90 additions & 0 deletions packages/di/test/dom-tree.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,53 @@ const dumb = function* () {};

define('test-dumb-child', dumb);

const greeter = {
fr: {
hello({ name }) {
return `Bonjour ${name}`;
},
},
es: {
hello({ name }) {
return `Hola ${name}`;
},
},
en: {
hello({ name }) {
return `Hello ${name}`;
},
},
};

const provideIntl = provide(({ $host }) => {
const lang = $host.getAttribute('lang') ?? 'en';
return {
intl() {
return greeter[lang.split('-')[0]] ?? greeter['en'];
},
};
});

define(
'lang-provider',
provideIntl(function* () {}),
);

define(
'hello-world',
inject(function* ({ services, $root, $host }) {
const { intl } = services;
try {
while (true) {
const input = yield;
$root.textContent = intl.hello({ name: input?.attributes?.name ?? '' });
}
} finally {
$host.isClean = true;
}
}),
);

test('DOM element that registers some injectables has "provider" attribute', async ({
eq,
}) => {
Expand Down Expand Up @@ -127,3 +174,46 @@ test(`provider element shadows parent's injectables`, async ({ eq }) => {
eq(injected.a, 'abis');
eq(injected.b, 'b');
});

test('data and attributes are passed along', async ({ eq }) => {
const testedElement = document.createElement('template');
testedElement.innerHTML = `
<lang-provider lang="es">
<hello-world id="es-root" name="lorenzofox"></hello-world>
<lang-provider lang="fr">
<hello-world id="fr-override" name="laurent"></hello-world>
</lang-provider>
</lang-provider>
<lang-provider lang="en">
<hello-world id="en-sibling-1" name="lorenzofox"></hello-world>
<hello-world id="en-sibling-2" name="bob"></hello-world>
</lang-provider>
`;
debug.appendChild(testedElement.content.cloneNode(true));
await nextTick();
const esRootEl = document.getElementById('es-root');
const frOverrideEl = document.getElementById('fr-override');
const enSibling1El = document.getElementById('en-sibling-1');
const enSibling2El = document.getElementById('en-sibling-2');
eq(esRootEl.textContent, 'Hola lorenzofox');
eq(frOverrideEl.textContent, 'Bonjour laurent');
eq(enSibling1El.textContent, 'Hello lorenzofox');
eq(enSibling2El.textContent, 'Hello bob');
});

test('provider gets injected its own registry', async ({ eq }) => {
let tested = false;
define(
'provider-injected',
provide({
a: 'hello',
})(function* ({ services }) {
eq(services.a, 'hello');
tested = true;
}),
);

debug.appendChild(document.createElement('provider-injected'));
await nextTick();
eq(tested, true);
});
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ A set of small libraries to build your UI framework [converting coroutines into

* [core](./packages/core): define web components from a coroutine (666 bytes)
* [view](./packages/view): use declarative template rather than imperative rendering logic (1645 bytes)
* [controllers](./packages/controllers): manage state updates from a controller function (313 bytes)
* [di](./packages/di): define provider elements an inject what they create into children elements (362 bytes)
* [controllers](./packages/controllers): manage state updates from a controller function (351 bytes)
* [di](./packages/di): define provider elements an inject what they create into children elements (408 bytes)

Sizes are in bytes (minified and gzipped)

0 comments on commit ecc14fa

Please sign in to comment.