Skip to content

Commit e1694a6

Browse files
committed
Move scopes and namespaces to separate files
1 parent bfcc154 commit e1694a6

13 files changed

+132
-130
lines changed

packages/interactivity/src/directives.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import {
2222
splitTask,
2323
isPlainObject,
2424
} from './utils';
25-
import { directive, getScope, getEvaluate, type DirectiveEntry } from './hooks';
25+
import { directive, getEvaluate, type DirectiveEntry } from './hooks';
26+
import { getScope } from './scopes';
2627

2728
// Assigned objects should be ignored during proxification.
2829
const contextAssignedObjects = new WeakMap();

packages/interactivity/src/hooks.tsx

+3-95
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ import {
1212
type ComponentChildren,
1313
} from 'preact';
1414
import { useRef, useCallback, useContext } from 'preact/hooks';
15-
import type { VNode, Context, RefObject } from 'preact';
15+
import type { VNode, Context } from 'preact';
1616

1717
/**
1818
* Internal dependencies
1919
*/
2020
import { store, stores, universalUnlock } from './store';
2121
import { warn } from './utils';
22+
import { getScope, setScope, resetScope, type Scope } from './scopes';
2223
export interface DirectiveEntry {
2324
value: string | object;
2425
namespace: string;
@@ -69,14 +70,7 @@ interface DirectiveOptions {
6970
priority?: number;
7071
}
7172

72-
interface Scope {
73-
evaluate: Evaluate;
74-
context: object;
75-
ref: RefObject< HTMLElement >;
76-
attributes: createElement.JSX.HTMLAttributes;
77-
}
78-
79-
interface Evaluate {
73+
export interface Evaluate {
8074
( entry: DirectiveEntry, ...args: any[] ): any;
8175
}
8276

@@ -101,92 +95,6 @@ interface DirectivesProps {
10195
// Main context.
10296
const context = createContext< any >( {} );
10397

104-
// Wrap the element props to prevent modifications.
105-
const immutableMap = new WeakMap();
106-
const immutableError = () => {
107-
throw new Error(
108-
'Please use `data-wp-bind` to modify the attributes of an element.'
109-
);
110-
};
111-
const immutableHandlers: ProxyHandler< object > = {
112-
get( target, key, receiver ) {
113-
const value = Reflect.get( target, key, receiver );
114-
return !! value && typeof value === 'object'
115-
? deepImmutable( value )
116-
: value;
117-
},
118-
set: immutableError,
119-
deleteProperty: immutableError,
120-
};
121-
const deepImmutable = < T extends object = {} >( target: T ): T => {
122-
if ( ! immutableMap.has( target ) ) {
123-
immutableMap.set( target, new Proxy( target, immutableHandlers ) );
124-
}
125-
return immutableMap.get( target );
126-
};
127-
128-
// Store stacks for the current scope and the default namespaces and export APIs
129-
// to interact with them.
130-
const scopeStack: Scope[] = [];
131-
const namespaceStack: string[] = [];
132-
133-
/**
134-
* Retrieves the context inherited by the element evaluating a function from the
135-
* store. The returned value depends on the element and the namespace where the
136-
* function calling `getContext()` exists.
137-
*
138-
* @param namespace Store namespace. By default, the namespace where the calling
139-
* function exists is used.
140-
* @return The context content.
141-
*/
142-
export const getContext = < T extends object >( namespace?: string ): T => {
143-
const scope = getScope();
144-
if ( ! scope ) {
145-
throw Error(
146-
'Cannot call `getContext()` outside getters and actions used by directives.'
147-
);
148-
}
149-
return scope.context[ namespace || getNamespace() ];
150-
};
151-
152-
/**
153-
* Retrieves a representation of the element where a function from the store
154-
* is being evalutated. Such representation is read-only, and contains a
155-
* reference to the DOM element, its props and a local reactive state.
156-
*
157-
* @return Element representation.
158-
*/
159-
export const getElement = () => {
160-
if ( ! getScope() ) {
161-
throw Error(
162-
'Cannot call `getElement()` outside getters and actions used by directives.'
163-
);
164-
}
165-
const { ref, attributes } = getScope();
166-
return Object.freeze( {
167-
ref: ref.current,
168-
attributes: deepImmutable( attributes ),
169-
} );
170-
};
171-
172-
export const getScope = () => scopeStack.slice( -1 )[ 0 ];
173-
174-
export const setScope = ( scope: Scope ) => {
175-
scopeStack.push( scope );
176-
};
177-
export const resetScope = () => {
178-
scopeStack.pop();
179-
};
180-
181-
export const getNamespace = () => namespaceStack.slice( -1 )[ 0 ];
182-
183-
export const setNamespace = ( namespace: string ) => {
184-
namespaceStack.push( namespace );
185-
};
186-
export const resetNamespace = () => {
187-
namespaceStack.pop();
188-
};
189-
19098
// WordPress Directives.
19199
const directiveCallbacks: Record< string, DirectiveCallback > = {};
192100
const directivePriorities: Record< string, number > = {};

packages/interactivity/src/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ import registerDirectives from './directives';
1111
import { init, getRegionRootFragment, initialVdom } from './init';
1212
import { directivePrefix } from './constants';
1313
import { toVdom } from './vdom';
14-
import { directive, getNamespace } from './hooks';
14+
import { directive } from './hooks';
15+
import { getNamespace } from './namespaces';
1516
import { parseInitialData, populateInitialData } from './store';
1617
import { proxifyState } from './proxies';
1718

1819
export { store, getConfig } from './store';
19-
export { getContext, getElement } from './hooks';
20+
export { getContext, getElement } from './scopes';
2021
export {
2122
withScope,
2223
useWatch,

packages/interactivity/src/init.ts

-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { hydrate, type ContainerNode, type ComponentChild } from 'preact';
88
import { toVdom, hydratedIslands } from './vdom';
99
import { createRootFragment, splitTask } from './utils';
1010
import { directivePrefix } from './constants';
11-
import { parseInitialData, populateInitialData } from './store';
1211

1312
// Keep the same root fragment for each interactive region node.
1413
const regionRootFragments = new WeakMap();
@@ -30,10 +29,6 @@ export const initialVdom = new WeakMap< Element, ComponentChild[] >();
3029

3130
// Initialize the router with the initial DOM.
3231
export const init = async () => {
33-
// Parse and populate the initial state and config.
34-
const data = parseInitialData();
35-
populateInitialData( data );
36-
3732
const nodes = document.querySelectorAll(
3833
`[data-${ directivePrefix }-interactive]`
3934
);
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const namespaceStack: string[] = [];
2+
3+
export const getNamespace = () => namespaceStack.slice( -1 )[ 0 ];
4+
5+
export const setNamespace = ( namespace: string ) => {
6+
namespaceStack.push( namespace );
7+
};
8+
export const resetNamespace = () => {
9+
namespaceStack.pop();
10+
};

packages/interactivity/src/proxies/signals.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
* Internal dependencies
1414
*/
1515
import { getProxyNs } from './registry';
16-
import { getScope, setNamespace, resetNamespace } from '../hooks';
16+
import { getScope } from '../scopes';
17+
import { setNamespace, resetNamespace } from '../namespaces';
1718
import { withScope } from '../utils';
1819

1920
/**

packages/interactivity/src/proxies/state.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { signal, type Signal } from '@preact/signals';
88
*/
99
import { createProxy, getProxy, getProxyNs, shouldProxy } from './registry';
1010
import { PropSignal } from './signals';
11-
import { setNamespace, resetNamespace } from '../hooks';
11+
import { setNamespace, resetNamespace } from '../namespaces';
1212

1313
/**
1414
* Set of built-in symbols.

packages/interactivity/src/proxies/store.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
* Internal dependencies
33
*/
44
import { createProxy, getProxyNs, shouldProxy } from './registry';
5-
import { setNamespace, resetNamespace } from '../hooks';
5+
/**
6+
* External dependencies
7+
*/
8+
import { setNamespace, resetNamespace } from '../namespaces';
69
import { withScope, isPlainObject } from '../utils';
710

811
/**

packages/interactivity/src/proxies/test/state-proxy.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,8 @@ import { effect } from '@preact/signals-core';
1010
* Internal dependencies
1111
*/
1212
import { proxifyState, peek } from '../';
13-
import {
14-
setScope,
15-
resetScope,
16-
setNamespace,
17-
resetNamespace,
18-
getContext,
19-
getElement,
20-
} from '../../hooks';
13+
import { setScope, resetScope, getContext, getElement } from '../../scopes';
14+
import { setNamespace, resetNamespace } from '../../namespaces';
2115

2216
type State = {
2317
a?: number;

packages/interactivity/src/proxies/test/store-proxy.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@
22
* Internal dependencies
33
*/
44
import { proxifyStore, proxifyState } from '../';
5-
import {
6-
setScope,
7-
resetScope,
8-
setNamespace,
9-
resetNamespace,
10-
getContext,
11-
} from '../../hooks';
5+
import { setScope, resetScope, getContext } from '../../scopes';
6+
import { setNamespace, resetNamespace } from '../../namespaces';
127

138
describe( 'interactivity api - store proxy', () => {
149
describe( 'get', () => {

packages/interactivity/src/scopes.ts

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import type { h as createElement, RefObject } from 'preact';
5+
6+
/**
7+
* Internal dependencies
8+
*/
9+
import { getNamespace } from './namespaces';
10+
import type { Evaluate } from './hooks';
11+
12+
export interface Scope {
13+
evaluate: Evaluate;
14+
context: object;
15+
ref: RefObject< HTMLElement >;
16+
attributes: createElement.JSX.HTMLAttributes;
17+
}
18+
19+
// Store stacks for the current scope and the default namespaces and export APIs
20+
// to interact with them.
21+
const scopeStack: Scope[] = [];
22+
23+
export const getScope = () => scopeStack.slice( -1 )[ 0 ];
24+
25+
export const setScope = ( scope: Scope ) => {
26+
scopeStack.push( scope );
27+
};
28+
export const resetScope = () => {
29+
scopeStack.pop();
30+
};
31+
32+
// Wrap the element props to prevent modifications.
33+
const immutableMap = new WeakMap();
34+
const immutableError = () => {
35+
throw new Error(
36+
'Please use `data-wp-bind` to modify the attributes of an element.'
37+
);
38+
};
39+
const immutableHandlers: ProxyHandler< object > = {
40+
get( target, key, receiver ) {
41+
const value = Reflect.get( target, key, receiver );
42+
return !! value && typeof value === 'object'
43+
? deepImmutable( value )
44+
: value;
45+
},
46+
set: immutableError,
47+
deleteProperty: immutableError,
48+
};
49+
const deepImmutable = < T extends object = {} >( target: T ): T => {
50+
if ( ! immutableMap.has( target ) ) {
51+
immutableMap.set( target, new Proxy( target, immutableHandlers ) );
52+
}
53+
return immutableMap.get( target );
54+
};
55+
56+
/**
57+
* Retrieves the context inherited by the element evaluating a function from the
58+
* store. The returned value depends on the element and the namespace where the
59+
* function calling `getContext()` exists.
60+
*
61+
* @param namespace Store namespace. By default, the namespace where the calling
62+
* function exists is used.
63+
* @return The context content.
64+
*/
65+
export const getContext = < T extends object >( namespace?: string ): T => {
66+
const scope = getScope();
67+
if ( ! scope ) {
68+
throw Error(
69+
'Cannot call `getContext()` outside getters and actions used by directives.'
70+
);
71+
}
72+
return scope.context[ namespace || getNamespace() ];
73+
};
74+
75+
/**
76+
* Retrieves a representation of the element where a function from the store
77+
* is being evalutated. Such representation is read-only, and contains a
78+
* reference to the DOM element, its props and a local reactive state.
79+
*
80+
* @return Element representation.
81+
*/
82+
export const getElement = () => {
83+
if ( ! getScope() ) {
84+
throw Error(
85+
'Cannot call `getElement()` outside getters and actions used by directives.'
86+
);
87+
}
88+
const { ref, attributes } = getScope();
89+
return Object.freeze( {
90+
ref: ref.current,
91+
attributes: deepImmutable( attributes ),
92+
} );
93+
};

packages/interactivity/src/store.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
* Internal dependencies
33
*/
44
import { proxifyState, proxifyStore } from './proxies';
5-
import { getNamespace } from './hooks';
5+
/**
6+
* External dependencies
7+
*/
8+
import { getNamespace } from './namespaces';
69
import { isPlainObject } from './utils';
710

811
const deepMerge = ( target: any, source: any ) => {
@@ -217,3 +220,7 @@ export const populateInitialData = ( data?: {
217220
} );
218221
}
219222
};
223+
224+
// Parse and populate the initial state and config.
225+
const data = parseInitialData();
226+
populateInitialData( data );

packages/interactivity/src/utils.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,8 @@ import { effect } from '@preact/signals';
1414
/**
1515
* Internal dependencies
1616
*/
17-
import {
18-
getScope,
19-
setScope,
20-
resetScope,
21-
getNamespace,
22-
setNamespace,
23-
resetNamespace,
24-
} from './hooks';
17+
import { getScope, setScope, resetScope } from './scopes';
18+
import { getNamespace, setNamespace, resetNamespace } from './namespaces';
2519

2620
interface Flusher {
2721
readonly flush: () => void;

0 commit comments

Comments
 (0)