Skip to content

Commit

Permalink
🏷️ Fixes Types
Browse files Browse the repository at this point in the history
  • Loading branch information
ekwoka committed Feb 28, 2024
1 parent 1c17ed6 commit 2ebafae
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 42 deletions.
2 changes: 1 addition & 1 deletion packages/scope/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,5 @@
"type": "git",
"url": "https://github.com/ekwoka/alpine-plugins"
},
"homepage": "https://github.com/ekwoka/alpine-plugins/blob/main/packages/xscope/README.md"
"homepage": "https://github.com/ekwoka/alpine-plugins/blob/main/packages/scope/README.md"
}
84 changes: 45 additions & 39 deletions packages/scope/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import type { Alpine } from 'alpinets';
import type { Alpine } from 'alpinejs';
import type { Assertion } from 'vitest';

export const Scope = (Alpine: Alpine) => {
const $scope = Symbol('$scope');
type Scopable = { [$scope]?: Map<string, HTMLElement> };
Alpine.directive('scope', (el, { expression }) => {
const context = Alpine.$data(el);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const context = Alpine.$data(el) as Scopable;

const rootContext = Alpine.closestDataStack(el)[0];
if (!rootContext) return;
rootContext[$scope] = new Map(
context[$scope] as Map<string, HTMLElement> | undefined,
).set(expression, el);
rootContext[$scope] = new Map(context[$scope]).set(expression, el);
});
Alpine.magic('scope', (el) => {
return new Proxy(
{},
{
get(_, name: string) {
const scopes = Alpine.$data(el)[$scope] as
| Map<string, HTMLElement>
| undefined;
const scopes = (Alpine.$data(el) as Scopable)[$scope];
if (scopes?.has(name)) return Alpine.$data(scopes.get(name)!);
const root = Alpine.findClosest(el, (el) =>
el.matches(`[x-data="${name}"]`),
);
) as HTMLElement | undefined;
if (root) return Alpine.$data(root);
return undefined;
},
Expand All @@ -46,13 +45,13 @@ if (import.meta.vitest) {
const root = await render(
`
<div x-data="foo">
<div x-data="bar">
<span id="naked" x-text="value"></span>
<span id="foo" x-text="$scope.foo?.value"></span>
<span id="bar" x-text="$scope.bar?.value"></span>
</div>
<div x-data="bar">
<span id="naked" x-text="value"></span>
<span id="foo" x-text="$scope.foo?.value"></span>
<span id="bar" x-text="$scope.bar?.value"></span>
</div>
</div>
`.trim(),
`.trim(),
)
.withComponent('foo', () => ({ value: 'foo' }))
.withComponent('bar', () => ({ value: 'bar' }))
Expand All @@ -70,14 +69,14 @@ if (import.meta.vitest) {
it('can access explicitely scoped context', async () => {
const root = await render(
`
<div x-data="{ value: 'foo' }" x-scope="foo">
<div x-data="{ value: 'bar' }" x-scope="bar">
<span id="naked" x-text="value"></span>
<span id="foo" x-text="$scope.foo?.value"></span>
<span id="bar" x-text="$scope.bar?.value"></span>
</div>
</div>
`.trim(),
<div x-data="{ value: 'foo' }" x-scope="foo">
<div x-data="{ value: 'bar' }" x-scope="bar">
<span id="naked" x-text="value"></span>
<span id="foo" x-text="$scope.foo?.value"></span>
<span id="bar" x-text="$scope.bar?.value"></span>
</div>
</div>
`.trim(),
).withPlugin(Scope);
expect((root as HTMLElement).querySelector('#naked')).toHaveTextContent(
'bar',
Expand All @@ -92,13 +91,13 @@ if (import.meta.vitest) {
it('favors explicitely scoped contexts', async () => {
const root = await render(
`
<div x-data="foo" x-scope="bar">
<div x-data="bar">
<span id="naked" x-text="value"></span>
<span id="bar" x-text="$scope.bar?.value"></span>
</div>
</div>
`.trim(),
<div x-data="foo" x-scope="bar">
<div x-data="bar">
<span id="naked" x-text="value"></span>
<span id="bar" x-text="$scope.bar?.value"></span>
</div>
</div>
`.trim(),
)
.withComponent('foo', () => ({ value: 'foo' }))
.withComponent('bar', () => ({ value: 'bar' }))
Expand All @@ -113,15 +112,15 @@ if (import.meta.vitest) {
it('does not leak', async () => {
const root = await render(
`
<div x-data="{ value: 'root' }">
<div x-data="foo" x-scope="foo"></div>
<div x-data="bar">
</div>
<span id="naked" x-text="value"></span>
<span id="foo" x-text="$scope.foo?.value ?? 'not found'"></span>
<span id="bar" x-text="$scope.bar?.value ?? 'not found'"></span>
</div>
`.trim(),
<div x-data="{ value: 'root' }">
<div x-data="foo" x-scope="foo"></div>
<div x-data="bar">
</div>
<span id="naked" x-text="value"></span>
<span id="foo" x-text="$scope.foo?.value ?? 'not found'"></span>
<span id="bar" x-text="$scope.bar?.value ?? 'not found'"></span>
</div>
`.trim(),
)
.withComponent('foo', () => ({ value: 'foo' }))
.withComponent('bar', () => ({ value: 'bar' }))
Expand All @@ -138,3 +137,10 @@ if (import.meta.vitest) {
});
});
}
declare module 'vitest' {
interface Assertion<T> extends AlpineMatchers<T> {}
}

interface AlpineMatchers<T> {
toHaveTextContent: (expected: string) => Assertion<T>;
}
4 changes: 2 additions & 2 deletions size.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"raw": 373
},
"brotli": {
"pretty": "238 B",
"raw": 238
"pretty": "236 B",
"raw": 236
}
},
"xajax": {
Expand Down

0 comments on commit 2ebafae

Please sign in to comment.