Set Refactor for Solid-react complete

main
Jackson Morgan 6 months ago
parent a659b88d7f
commit de5d056f7a
  1. 30
      packages/jsonld-dataset-proxy/src/ProxyContext.ts
  2. 1
      packages/jsonld-dataset-proxy/src/index.ts
  3. 4
      packages/solid-react/src/useMatchObject.ts
  4. 4
      packages/solid-react/src/useMatchSubject.ts
  5. 87
      packages/solid-react/src/util/TrackingProxyContext.ts
  6. 56
      packages/solid-react/src/util/TrackingSetProxy.ts
  7. 43
      packages/solid-react/src/util/TrackingSubjectProxy.ts
  8. 8
      packages/solid-react/test/.ldo/post.typings.ts
  9. 14
      packages/solid-react/test/Integration.test.tsx

@ -44,17 +44,17 @@ export class ProxyContext {
public createSubjectProxy(node: NamedNode | BlankNode): SubjectProxy { public createSubjectProxy(node: NamedNode | BlankNode): SubjectProxy {
if (!this.subjectMap.has(node.value)) { if (!this.subjectMap.has(node.value)) {
const proxy = new Proxy( const proxy = this.createNewSubjectProxy(node);
{ "@id": node },
this.createSubjectHandler(),
) as unknown as SubjectProxy;
this.subjectMap.set(node.value, proxy); this.subjectMap.set(node.value, proxy);
} }
return this.subjectMap.get(node.value) as SubjectProxy; return this.subjectMap.get(node.value) as SubjectProxy;
} }
protected createSubjectHandler() { protected createNewSubjectProxy(node: NamedNode | BlankNode): SubjectProxy {
return createSubjectHandler(this); return new Proxy(
{ "@id": node },
createSubjectHandler(this),
) as unknown as SubjectProxy;
} }
private getSetKey(...quadMatch: QuadMatch) { private getSetKey(...quadMatch: QuadMatch) {
@ -72,10 +72,9 @@ export class ProxyContext {
): SetProxy { ): SetProxy {
const key = this.getSetKey(...quadMatch); const key = this.getSetKey(...quadMatch);
if (!this.setMap.has(key)) { if (!this.setMap.has(key)) {
const proxy = createNewSetProxy( const proxy = this.createNewSetProxy(
quadMatch, quadMatch,
isSubjectOriented ?? false, isSubjectOriented,
this,
isLangStringSet, isLangStringSet,
); );
this.setMap.set(key, proxy); this.setMap.set(key, proxy);
@ -83,6 +82,19 @@ export class ProxyContext {
return this.setMap.get(key)!; return this.setMap.get(key)!;
} }
protected createNewSetProxy(
quadMatch: QuadMatch,
isSubjectOriented?: boolean,
isLangStringSet?: boolean,
) {
return createNewSetProxy(
quadMatch,
isSubjectOriented ?? false,
this,
isLangStringSet,
);
}
public duplicate(alternativeOptions: Partial<ProxyContextOptions>) { public duplicate(alternativeOptions: Partial<ProxyContextOptions>) {
const fullOptions: ProxyContextOptions = { const fullOptions: ProxyContextOptions = {
...{ ...{

@ -17,6 +17,7 @@ export * from "./language/languageSet";
export * from "./language/languageTypes"; export * from "./language/languageTypes";
export * from "./language/languageUtils"; export * from "./language/languageUtils";
export * from "./setProxy/createNewSetProxy";
export * from "./setProxy/isSetProxy"; export * from "./setProxy/isSetProxy";
export * from "./setProxy/SetProxy"; export * from "./setProxy/SetProxy";
export * from "./setProxy/set"; export * from "./setProxy/set";

@ -1,4 +1,4 @@
import type { LdoBase, ShapeType } from "@ldo/ldo"; import type { LdoBase, LdSet, ShapeType } from "@ldo/ldo";
import type { QuadMatch } from "@ldo/rdf-utils"; import type { QuadMatch } from "@ldo/rdf-utils";
import type { LdoBuilder } from "@ldo/ldo"; import type { LdoBuilder } from "@ldo/ldo";
import { useCallback } from "react"; import { useCallback } from "react";
@ -9,7 +9,7 @@ export function useMatchObject<Type extends LdoBase>(
subject?: QuadMatch[0] | string, subject?: QuadMatch[0] | string,
predicate?: QuadMatch[1] | string, predicate?: QuadMatch[1] | string,
graph?: QuadMatch[3] | string, graph?: QuadMatch[3] | string,
): Type[] { ): LdSet<Type> {
const matchObject = useCallback( const matchObject = useCallback(
(builder: LdoBuilder<Type>) => { (builder: LdoBuilder<Type>) => {
return builder.matchObject(subject, predicate, graph); return builder.matchObject(subject, predicate, graph);

@ -1,4 +1,4 @@
import type { LdoBase, ShapeType } from "@ldo/ldo"; import type { LdoBase, LdSet, ShapeType } from "@ldo/ldo";
import type { QuadMatch } from "@ldo/rdf-utils"; import type { QuadMatch } from "@ldo/rdf-utils";
import type { LdoBuilder } from "@ldo/ldo"; import type { LdoBuilder } from "@ldo/ldo";
import { useCallback } from "react"; import { useCallback } from "react";
@ -9,7 +9,7 @@ export function useMatchSubject<Type extends LdoBase>(
predicate?: QuadMatch[1] | string, predicate?: QuadMatch[1] | string,
object?: QuadMatch[2] | string, object?: QuadMatch[2] | string,
graph?: QuadMatch[3] | string, graph?: QuadMatch[3] | string,
): Type[] { ): LdSet<Type> {
const matchSubject = useCallback( const matchSubject = useCallback(
(builder: LdoBuilder<Type>) => { (builder: LdoBuilder<Type>) => {
return builder.matchSubject(predicate, object, graph); return builder.matchSubject(predicate, object, graph);

@ -1,13 +1,14 @@
import type { import type {
ArrayProxyTarget,
SubjectProxyTarget,
ProxyContextOptions, ProxyContextOptions,
SubjectProxy,
SetProxy,
} from "@ldo/jsonld-dataset-proxy"; } from "@ldo/jsonld-dataset-proxy";
import { ProxyContext } from "@ldo/jsonld-dataset-proxy"; import { ProxyContext } from "@ldo/jsonld-dataset-proxy";
import type { QuadMatch } from "@ldo/rdf-utils"; import type { QuadMatch } from "@ldo/rdf-utils";
import type { SubscribableDataset } from "@ldo/subscribable-dataset"; import type { SubscribableDataset } from "@ldo/subscribable-dataset";
import { namedNode } from "@rdfjs/data-model"; import type { BlankNode, NamedNode, Quad } from "@rdfjs/types";
import type { Quad } from "@rdfjs/types"; import { createTrackingSubjectProxy } from "./TrackingSubjectProxy";
import { createTrackingSetProxy } from "./TrackingSetProxy";
/** /**
* @internal * @internal
@ -34,77 +35,27 @@ export class TrackingProxyContext extends ProxyContext {
} }
// Adds the listener to the subscribable dataset while ensuring deduping of the listener // Adds the listener to the subscribable dataset while ensuring deduping of the listener
private addListener(eventName: QuadMatch) { public addListener(eventName: QuadMatch) {
const listeners = this.subscribableDataset.listeners(eventName); const listeners = this.subscribableDataset.listeners(eventName);
if (!listeners.includes(this.listener)) { if (!listeners.includes(this.listener)) {
this.subscribableDataset.on(eventName, this.listener); this.subscribableDataset.on(eventName, this.listener);
} }
} }
protected createSubjectHandler(): ProxyHandler<SubjectProxyTarget> { protected createNewSubjectProxy(node: NamedNode | BlankNode): SubjectProxy {
const baseHandler = super.createSubjectHandler(); return createTrackingSubjectProxy(this, node);
const oldGetFunction = baseHandler.get;
const newGetFunction: ProxyHandler<SubjectProxyTarget>["get"] = (
target: SubjectProxyTarget,
key: string | symbol,
receiver,
) => {
const subject = target["@id"];
const rdfTypes = this.getRdfType(subject);
if (typeof key === "symbol") {
// Do Nothing
} else if (key === "@id") {
this.addListener([subject, null, null, null]);
} else if (!this.contextUtil.isArray(key, rdfTypes)) {
const predicate = namedNode(this.contextUtil.keyToIri(key, rdfTypes));
this.addListener([subject, predicate, null, null]);
}
return oldGetFunction && oldGetFunction(target, key, receiver);
};
baseHandler.get = newGetFunction;
baseHandler.set = () => {
console.warn(
"You've attempted to set a value on a Linked Data Object from the useSubject, useMatchingSubject, or useMatchingObject hooks. These linked data objects should only be used to render data, not modify it. To modify data, use the `changeData` function.",
);
return true;
};
return baseHandler;
} }
protected createArrayHandler(): ProxyHandler<ArrayProxyTarget> { protected createNewSetProxy(
const baseHandler = super.createArrayHandler(); quadMatch: QuadMatch,
const oldGetFunction = baseHandler.get; isSubjectOriented?: boolean,
const newGetFunction: ProxyHandler<ArrayProxyTarget>["get"] = ( isLangStringSet?: boolean,
target: ArrayProxyTarget, ): SetProxy {
key: string | symbol, return createTrackingSetProxy(
receiver, this,
) => { quadMatch,
if (qualifiedArrayMethods.has(key)) { isSubjectOriented,
this.addListener([target[0][0], target[0][1], target[0][2], null]); isLangStringSet,
} );
return oldGetFunction && oldGetFunction(target, key, receiver);
};
baseHandler.get = newGetFunction;
return baseHandler;
} }
} }
const qualifiedArrayMethods = new Set([
"forEach",
"map",
"reduce",
Symbol.iterator,
"entries",
"every",
"filter",
"find",
"findIndex",
"findLast",
"findLastIndex",
"includes, indexOf",
"keys",
"lastIndexOf",
"reduceRight",
"some",
"values",
]);

@ -0,0 +1,56 @@
import { createNewSetProxy, type SetProxy } from "@ldo/jsonld-dataset-proxy";
import type { TrackingProxyContext } from "./TrackingProxyContext";
import type { QuadMatch } from "@ldo/rdf-utils";
export function createTrackingSetProxy(
proxyContext: TrackingProxyContext,
quadMatch: QuadMatch,
isSubjectOriented?: boolean,
isLangStringSet?: boolean,
): SetProxy {
const baseSetProxy = createNewSetProxy(
quadMatch,
isSubjectOriented ?? false,
proxyContext,
isLangStringSet,
);
return new Proxy(baseSetProxy, {
get: (target: SetProxy, key: string | symbol, receiver) => {
if (trackingMethods.has(key)) {
proxyContext.addListener(quadMatch);
} else if (disallowedMethods.has(key)) {
console.warn(
"You've attempted to modify a value on a Linked Data Object from the useSubject, useMatchingSubject, or useMatchingObject hooks. These linked data objects should only be used to render data, not modify it. To modify data, use the `changeData` function.",
);
}
return Reflect.get(target, key, receiver);
},
});
}
const trackingMethods = new Set([
"has",
"size",
"entries",
"keys",
"values",
Symbol.iterator,
"every",
"every",
"some",
"forEach",
"map",
"reduce",
"toArray",
"toJSON",
"difference",
"intersection",
"isDisjointFrom",
"isSubsetOf",
"isSupersetOf",
"symmetricDifference",
"union",
]);
const disallowedMethods = new Set<string | symbol>(["add", "clear", "delete"]);

@ -0,0 +1,43 @@
import type { SubjectProxyTarget } from "@ldo/jsonld-dataset-proxy";
import {
createSubjectHandler,
type SubjectProxy,
} from "@ldo/jsonld-dataset-proxy";
import type { BlankNode, NamedNode } from "@rdfjs/types";
import type { TrackingProxyContext } from "./TrackingProxyContext";
import { namedNode } from "@rdfjs/data-model";
export function createTrackingSubjectProxy(
proxyContext: TrackingProxyContext,
node: NamedNode | BlankNode,
): SubjectProxy {
const baseHandler = createSubjectHandler(proxyContext);
const oldGetFunction = baseHandler.get;
const newGetFunction: ProxyHandler<SubjectProxyTarget>["get"] = (
target: SubjectProxyTarget,
key: string | symbol,
receiver,
) => {
const subject = target["@id"];
const rdfTypes = proxyContext.getRdfType(subject);
if (typeof key === "symbol") {
// Do Nothing
} else if (key === "@id") {
proxyContext.addListener([subject, null, null, null]);
} else if (!proxyContext.contextUtil.isSet(key, rdfTypes)) {
const predicate = namedNode(
proxyContext.contextUtil.keyToIri(key, rdfTypes),
);
proxyContext.addListener([subject, predicate, null, null]);
}
return oldGetFunction && oldGetFunction(target, key, receiver);
};
baseHandler.get = newGetFunction;
baseHandler.set = () => {
console.warn(
"You've attempted to set a value on a Linked Data Object from the useSubject, useMatchingSubject, or useMatchingObject hooks. These linked data objects should only be used to render data, not modify it. To modify data, use the `changeData` function.",
);
return true;
};
return new Proxy({ "@id": node }, baseHandler) as unknown as SubjectProxy;
}

@ -1,4 +1,4 @@
import { ContextDefinition } from "jsonld"; import { LdSet, LdoJsonldContext } from "@ldo/ldo";
/** /**
* ============================================================================= * =============================================================================
@ -11,7 +11,7 @@ import { ContextDefinition } from "jsonld";
*/ */
export interface PostSh { export interface PostSh {
"@id"?: string; "@id"?: string;
"@context"?: ContextDefinition; "@context"?: LdoJsonldContext;
type: type:
| { | {
"@id": "SocialMediaPosting"; "@id": "SocialMediaPosting";
@ -39,7 +39,7 @@ export interface PostSh {
/** /**
* The publisher of the creative work. * The publisher of the creative work.
*/ */
publisher: { publisher: LdSet<{
"@id": string; "@id": string;
}[]; }>;
} }

@ -252,7 +252,7 @@ describe("Integration Tests", () => {
await screen.findByText("test"); await screen.findByText("test");
}); });
it("renders the array value from the useSubject value", async () => { it("renders the set value from the useSubject value", async () => {
const UseSubjectTest: FunctionComponent = () => { const UseSubjectTest: FunctionComponent = () => {
const resource = useResource(SAMPLE_DATA_URI); const resource = useResource(SAMPLE_DATA_URI);
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`);
@ -260,7 +260,7 @@ describe("Integration Tests", () => {
return ( return (
<div> <div>
<p role="single">{post.publisher[0]["@id"]}</p> <p role="single">{post.publisher.toArray()[0]["@id"]}</p>
<ul role="list"> <ul role="list">
{post.publisher.map((publisher) => { {post.publisher.map((publisher) => {
return <li key={publisher["@id"]}>{publisher["@id"]}</li>; return <li key={publisher["@id"]}>{publisher["@id"]}</li>;
@ -349,7 +349,12 @@ describe("Integration Tests", () => {
return ( return (
<div> <div>
<p role="value">{post.articleBody}</p> <p role="value">{post.articleBody}</p>
<button onClick={() => (post.articleBody = "bad")}> <button
onClick={() => {
post.articleBody = "bad";
post.publisher.add({ "@id": "example" });
}}
>
Attempt Change Attempt Change
</button> </button>
</div> </div>
@ -368,6 +373,9 @@ describe("Integration Tests", () => {
expect(warn).toHaveBeenCalledWith( expect(warn).toHaveBeenCalledWith(
"You've attempted to set a value on a Linked Data Object from the useSubject, useMatchingSubject, or useMatchingObject hooks. These linked data objects should only be used to render data, not modify it. To modify data, use the `changeData` function.", "You've attempted to set a value on a Linked Data Object from the useSubject, useMatchingSubject, or useMatchingObject hooks. These linked data objects should only be used to render data, not modify it. To modify data, use the `changeData` function.",
); );
expect(warn).toHaveBeenCalledWith(
"You've attempted to modify a value on a Linked Data Object from the useSubject, useMatchingSubject, or useMatchingObject hooks. These linked data objects should only be used to render data, not modify it. To modify data, use the `changeData` function.",
);
warn.mockReset(); warn.mockReset();
}); });

Loading…
Cancel
Save