Skip to content

Commit 7128cee

Browse files
authored
Merge pull request #21 from hypermedia-app/inline-graph-init
Improve initialising `rdf-graph` from `window.graphs`
2 parents 74ee6e5 + 82bcf0e commit 7128cee

21 files changed

Lines changed: 471 additions & 428 deletions

‎.changeset/famous-games-lead.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"lit-rdf": patch
3+
---
4+
5+
No actual `custom-elements.js` module caused issues at runtime
6+

‎.changeset/frank-facts-end.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"lit-rdf": patch
3+
---
4+
5+
`rdf-graph` now initializes itself from 'window.graphs`
6+

‎.changeset/pink-doors-lie.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"lit-rdf": patch
3+
---
4+
5+
Removed `data-graph.js` (technically breaking change but the script does not work in 4.0 anyway)

‎.storybook/load-graph.ts‎

Lines changed: 36 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,56 @@
1-
import $rdf from '@zazuko/env/web.js'
1+
import $rdf from '@zazuko/env/web'
2+
import isUrl from 'is-url'
23
import formats from '@rdfjs/formats'
34
import { Readable } from 'readable-stream'
4-
import type { DatasetCore, Quad } from '@rdfjs/types'
5-
import '../src/custom-elements.d.ts'
5+
import type { DatasetCore } from '@rdfjs/types'
66

77
$rdf.formats.import(formats)
88

9-
declare module '@rdfjs/types' {
10-
interface Stream extends AsyncGenerator<Quad> {}
11-
}
12-
13-
const scriptGraphs = new WeakMap<HTMLScriptElement, DatasetCore>()
14-
15-
async function parseGraphs() {
16-
const graphs: Record<string, DatasetCore> = {}
9+
const graphCache = new Map<string, DatasetCore>()
1710

18-
for (const script of document.querySelectorAll<HTMLScriptElement>('script[data-graph]')) {
19-
const graphName = script.getAttribute('data-graph')
20-
const mediaType = script.getAttribute('type')
21-
22-
if (mediaType && graphName) {
23-
let graph = scriptGraphs.get(script)
24-
if (!graph) {
25-
graph = await parseOrFetch(script, mediaType)
26-
scriptGraphs.set(script, graph)
27-
}
28-
29-
if (graph) {
30-
graphs[graphName] = graph
31-
}
32-
}
11+
export async function loadGraphData(contentOrUrl: string | URL): Promise<DatasetCore> {
12+
const key = typeof contentOrUrl === 'string' ? contentOrUrl : contentOrUrl.toString()
13+
const cached = graphCache.get(key)
14+
if (cached) {
15+
return cached
3316
}
3417

35-
return graphs
36-
}
37-
38-
async function parseOrFetch(script: HTMLScriptElement, mediaType: string) {
3918
const dataset = $rdf.dataset()
40-
let content: string | null | undefined
41-
42-
if (script.src) {
43-
const response = await fetch(script.src)
19+
let content = key
20+
let mediaType = 'text/turtle'
21+
22+
if (isUrl(content) || content.startsWith('http://') || content.startsWith('https://')) {
23+
const url = content
24+
const response = await fetch(url)
25+
const contentType = response.headers.get('content-type')
26+
if (contentType) {
27+
mediaType = contentType.split(';')[0]
28+
}
29+
if (url.endsWith('.nq') || url.endsWith('.nquads')) {
30+
mediaType = 'application/n-quads'
31+
}
32+
else if (url.endsWith('.nt')) {
33+
mediaType = 'application/n-triples'
34+
}
35+
else if (url.endsWith('.json') || url.endsWith('.jsonld')) {
36+
mediaType = 'application/ld+json'
37+
}
38+
else if (url.endsWith('.trig')) {
39+
mediaType = 'application/trig'
40+
}
41+
else if (url.endsWith('.ttl')) {
42+
mediaType = 'text/turtle'
43+
}
4444
content = await response.text()
4545
}
46-
else {
47-
content = script.textContent
48-
}
4946

5047
const stream = $rdf.formats.parsers.import(mediaType, Readable.from(content))
5148
if (stream) {
5249
for await (const quad of stream) {
5350
dataset.add(quad)
5451
}
5552
}
53+
54+
graphCache.set(key, dataset)
5655
return dataset
5756
}
58-
59-
const mutationObserver = new MutationObserver(async () => {
60-
const graphs = await parseGraphs()
61-
62-
const targets = document.querySelectorAll('rdf-graph')
63-
64-
for (const target of targets) {
65-
const graphName = target.id
66-
if (!graphName) {
67-
continue
68-
}
69-
if (graphs[graphName]) {
70-
target.value = graphs[graphName]
71-
}
72-
}
73-
})
74-
75-
mutationObserver.observe(document.body, { childList: true, subtree: true })

‎.storybook/preview.ts‎

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import type { Preview } from '@storybook/web-components-vite'
2-
import $rdf from '@zazuko/env/web.js'
3-
import stringToStream from 'string-to-stream'
4-
5-
import './load-graph.js'
6-
import type { Quad } from '@rdfjs/types'
2+
import type { DatasetCore, Quad } from '@rdfjs/types'
3+
import type { ArgTypesEnhancer } from 'storybook/internal/types'
4+
import { loadGraphData } from './load-graph.js'
75

86
declare module '@rdfjs/types' {
97
interface Stream extends AsyncGenerator<Quad> {}
@@ -23,20 +21,44 @@ const preview: Preview = {
2321
},
2422
},
2523
},
26-
loaders: [async ({ args }: Record<string, any>) => { // eslint-disable-line @typescript-eslint/no-explicit-any
27-
const dataset = $rdf.dataset()
28-
let data = $rdf.clownface({ dataset })
29-
if (args.data) {
30-
const stream = $rdf.formats.parsers.import('text/turtle', stringToStream(args.data.toString()))!
31-
for await (const quad of stream) {
32-
dataset.add(quad)
33-
}
34-
}
24+
loaders: [
25+
async ({ args }: Record<string, any>) => { // eslint-disable-line @typescript-eslint/no-explicit-any
26+
const loadPromises = Object.entries(args)
27+
.filter(([name]) => name.startsWith('graph'))
28+
.map(async ([name, source]) => {
29+
const dataset = await loadGraphData(source as string | URL)
30+
return [name, dataset] as [string, DatasetCore]
31+
})
3532

36-
return {
37-
data,
38-
}
39-
}],
33+
return Object.fromEntries(await Promise.all(loadPromises))
34+
},
35+
],
4036
}
4137

38+
export const argTypesEnhancers: ArgTypesEnhancer[] = [
39+
(context) => {
40+
const processedArgTypes = { ...context.argTypes }
41+
42+
const namedArgs = Object.keys(processedArgTypes)
43+
const actualArgs = Object.keys(context.initialArgs || {});
44+
45+
// 2. Scan only the real properties that exist on this component
46+
[...namedArgs, ...actualArgs].forEach((key) => {
47+
if (key.startsWith('graph')) {
48+
processedArgTypes[key] = {
49+
type: { name: 'string' },
50+
...processedArgTypes[key],
51+
control: { type: 'text' },
52+
table: {
53+
...processedArgTypes[key]?.table,
54+
category: 'Graph Data', // Safely isolate into the group
55+
},
56+
}
57+
}
58+
})
59+
60+
return processedArgTypes
61+
},
62+
]
63+
4264
export default preview

‎package.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
"./constructor.js": "./src/constructor.js",
3434
"./mixins.js": "./src/mixins.js",
3535
"./controllers.js": "./src/controllers.js",
36-
"./runtime/*.js": "./src/runtime/*.js",
3736
"./sort.js": "./src/sort.js"
3837
},
3938
"dependencies": {

‎src/components/rdf-graph.ts‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { LitElement, nothing } from 'lit'
22
import { customElement, property, state } from 'lit/decorators.js'
3-
import type { DatasetCore, NamedNode } from '@rdfjs/types'
3+
import type { DataFactory, DatasetCore, NamedNode, Quad } from '@rdfjs/types'
44
import { consume } from '@lit/context'
55
import type { DatasetProvider } from '../context.js'
66
import { datasetProvider } from '../context.js'
7+
import { Environment } from '../controllers/Environment.js'
78
import { toNamedNode } from '../converter.js'
89

10+
declare global {
11+
interface Window {
12+
graphs?: Record<string, (options: { factory: DataFactory }) => Quad[]>
13+
}
14+
}
15+
916
/**
1017
* Custom element (`<rdf-graph>`) that contributes an RDF graph dataset to an ancestor
1118
* `DatasetProvider` (such as `<rdf-dataset>`).
@@ -14,6 +21,8 @@ import { toNamedNode } from '../converter.js'
1421
*/
1522
@customElement('rdf-graph')
1623
export default class RdfGraph extends LitElement {
24+
private readonly env: Environment
25+
1726
/**
1827
* The RDF/JS dataset containing the graph triples to contribute to the parent dataset provider.
1928
*/
@@ -30,6 +39,12 @@ export default class RdfGraph extends LitElement {
3039
@consume({ context: datasetProvider })
3140
private datasetProvider: DatasetProvider | undefined
3241

42+
constructor() {
43+
super()
44+
45+
this.env = new Environment(this)
46+
}
47+
3348
protected updated(properties: Map<string, unknown>) {
3449
if (properties.has('value')) {
3550
if (this.value) {
@@ -43,6 +58,9 @@ export default class RdfGraph extends LitElement {
4358

4459
connectedCallback() {
4560
super.connectedCallback()
61+
if (!this.value && this.id && window.graphs?.[this.id]) {
62+
this.value = this.env.value.dataset(window.graphs[this.id]({ factory: this.env.value }))
63+
}
4664
if (this.value) {
4765
this.datasetProvider?.updateGraph(this, this.value, this.graph)
4866
}

‎src/custom-elements.d.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ interface HTMLRdfEnvironmentElement extends HTMLElement {
5050

5151

5252
interface HTMLRdfGraphElement extends HTMLElement {
53+
env: Environment;
5354
/**
5455
* The RDF/JS dataset containing the graph triples to contribute to the parent dataset provider.
5556
*/

‎src/custom-elements.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {}

‎src/runtime/data-graph.ts‎

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)