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

@ -17,6 +17,7 @@ export * from "./language/languageSet";
export * from "./language/languageTypes";
export * from "./language/languageUtils";
export * from "./setProxy/createNewSetProxy";
export * from "./setProxy/isSetProxy";
export * from "./setProxy/SetProxy";
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 { LdoBuilder } from "@ldo/ldo";
import { useCallback } from "react";
@ -9,7 +9,7 @@ export function useMatchObject<Type extends LdoBase>(
subject?: QuadMatch[0] | string,
predicate?: QuadMatch[1] | string,
graph?: QuadMatch[3] | string,
): Type[] {
): LdSet<Type> {
const matchObject = useCallback(
(builder: LdoBuilder<Type>) => {
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 { LdoBuilder } from "@ldo/ldo";
import { useCallback } from "react";
@ -9,7 +9,7 @@ export function useMatchSubject<Type extends LdoBase>(
predicate?: QuadMatch[1] | string,
object?: QuadMatch[2] | string,
graph?: QuadMatch[3] | string,
): Type[] {
): LdSet<Type> {
const matchSubject = useCallback(
(builder: LdoBuilder<Type>) => {
return builder.matchSubject(predicate, object, graph);

@ -1,13 +1,14 @@
import type {
ArrayProxyTarget,
SubjectProxyTarget,
ProxyContextOptions,
SubjectProxy,
SetProxy,
} from "@ldo/jsonld-dataset-proxy";
import { ProxyContext } from "@ldo/jsonld-dataset-proxy";
import type { QuadMatch } from "@ldo/rdf-utils";
import type { SubscribableDataset } from "@ldo/subscribable-dataset";
import { namedNode } from "@rdfjs/data-model";
import type { Quad } from "@rdfjs/types";
import type { BlankNode, NamedNode, Quad } from "@rdfjs/types";
import { createTrackingSubjectProxy } from "./TrackingSubjectProxy";
import { createTrackingSetProxy } from "./TrackingSetProxy";
/**
* @internal
@ -34,77 +35,27 @@ export class TrackingProxyContext extends ProxyContext {
}
// 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);
if (!listeners.includes(this.listener)) {
this.subscribableDataset.on(eventName, this.listener);
}
}
protected createSubjectHandler(): ProxyHandler<SubjectProxyTarget> {
const baseHandler = super.createSubjectHandler();
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 createNewSubjectProxy(node: NamedNode | BlankNode): SubjectProxy {
return createTrackingSubjectProxy(this, node);
}
protected createArrayHandler(): ProxyHandler<ArrayProxyTarget> {
const baseHandler = super.createArrayHandler();
const oldGetFunction = baseHandler.get;
const newGetFunction: ProxyHandler<ArrayProxyTarget>["get"] = (
target: ArrayProxyTarget,
key: string | symbol,
receiver,
) => {
if (qualifiedArrayMethods.has(key)) {
this.addListener([target[0][0], target[0][1], target[0][2], null]);
}
return oldGetFunction && oldGetFunction(target, key, receiver);
};
baseHandler.get = newGetFunction;
return baseHandler;
protected createNewSetProxy(
quadMatch: QuadMatch,
isSubjectOriented?: boolean,
isLangStringSet?: boolean,
): SetProxy {
return createTrackingSetProxy(
this,
quadMatch,
isSubjectOriented,
isLangStringSet,
);
}
}
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 {
"@id"?: string;
"@context"?: ContextDefinition;
"@context"?: LdoJsonldContext;
type:
| {
"@id": "SocialMediaPosting";
@ -39,7 +39,7 @@ export interface PostSh {
/**
* The publisher of the creative work.
*/
publisher: {
publisher: LdSet<{
"@id": string;
}[];
}>;
}

@ -252,7 +252,7 @@ describe("Integration Tests", () => {
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 resource = useResource(SAMPLE_DATA_URI);
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`);
@ -260,7 +260,7 @@ describe("Integration Tests", () => {
return (
<div>
<p role="single">{post.publisher[0]["@id"]}</p>
<p role="single">{post.publisher.toArray()[0]["@id"]}</p>
<ul role="list">
{post.publisher.map((publisher) => {
return <li key={publisher["@id"]}>{publisher["@id"]}</li>;
@ -349,7 +349,12 @@ describe("Integration Tests", () => {
return (
<div>
<p role="value">{post.articleBody}</p>
<button onClick={() => (post.articleBody = "bad")}>
<button
onClick={() => {
post.articleBody = "bad";
post.publisher.add({ "@id": "example" });
}}
>
Attempt Change
</button>
</div>
@ -368,6 +373,9 @@ describe("Integration Tests", () => {
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.",
);
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();
});

Loading…
Cancel
Save