Skip to content

Commit

Permalink
feat!: extension must be explicit
Browse files Browse the repository at this point in the history
  • Loading branch information
tpluscode committed Nov 30, 2023
1 parent 949b4c9 commit 4cdaf66
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 33 deletions.
5 changes: 5 additions & 0 deletions .changeset/shaggy-grapes-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"rdf-transform-graph-imports": minor
---

When importing local files, add support for `code:extension` to keep `code:imports` same in both local and web documents
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ root = true
indent_size = 2
indent_style = space
insert_final_newline = true

[*.snap]
indent_style = tab
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ PREFIX sh: <http://www.w3.org/ns/shacl#>
[
# relative URIs are relative to the document itself
code:imports <./property/identifier> ;
code:imports <./property/identifier.ttl> ;
code:imports <http://example.com/required-property> ;
] .
Expand Down Expand Up @@ -77,3 +77,22 @@ const dataset = await rdf.dataset().import(stream.pipe(imports(rdf, {
basePath: '/path/to/shape.ttl'
})))
```

### Reusing imports for local and remote documents

You may face the situation that you want to import the same file from a local file and a remote resource
but do not publish the extension in the remote resource's URL. In this case, you must add a `code:extension`
property to the import.

```turtle
PREFIX code: <https://code.described.at/>
[
code:imports <./property/identifier> ;
code:extension "ttl" ;
] .
```

If the above is a local file, e.g. `/path/to/shape.ttl`, the import will be resolved as `/path/to/property/identifier.ttl`.

If the above is a remote resource, e.g. `https://example.com/shape`, the import will be resolved as `https://example.com/property/identifier.ttl`.
30 changes: 22 additions & 8 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import through2 from 'through2'
import type { AnyPointer } from 'clownface'
import type { Quad } from '@rdfjs/types'
import { resolveImport } from './lib/path.js'
import Environment from './lib/env.js'
Expand All @@ -11,24 +12,37 @@ interface Options {
function transform(env: Environment, { basePath }: Options = {}) {
const code = env.namespace('https://code.described.at/')

const importStatements: AnyPointer = env.clownface()

return through2.obj(async function (quad: Quad, _, done) {
if (quad.predicate.equals(code.imports)) {
try {
const importTarget = resolveImport(quad.object, basePath)
if (quad.predicate.equals(code.imports) || quad.predicate.equals(code.extension)) {
importStatements.dataset.add(quad)
return done()
}

done(null, quad)
}, async function (done) {
try {
const imports = importStatements.has(code.imports)
.map(Import => {
const importPath = Import.out(code.imports).term!
const extension = Import.out(code.extension).value
return resolveImport(importPath, { basePath, extension })
})

for (const importTarget of imports) {
const importStream = fetchImport(env, importTarget)
.pipe(transform(env, { basePath: importTarget }))

for await (const importedQuad of importStream) {
this.push(importedQuad)
}

return done()
} catch (e) {
return done(e)
}
} catch (e: unknown) {
this.destroy(new Error(`Failed to import: ${e}`))
}

done(null, quad)
done()
})
}

Expand Down
3 changes: 2 additions & 1 deletion lib/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Environment as RdfjsEnvironment } from '@rdfjs/environment/Environ
import type { NamespaceFactory } from '@rdfjs/namespace/Factory.js'
import type FsUtilsFactory from '@zazuko/rdf-utils-fs/Factory.js'
import type { FetchFactory } from '@rdfjs/fetch-lite/Factory.js'
import type ClownfaceFactory from 'clownface/Factory.js'

type Environment = RdfjsEnvironment<NamespaceFactory | FsUtilsFactory | FetchFactory>
type Environment = RdfjsEnvironment<NamespaceFactory | FsUtilsFactory | FetchFactory | ClownfaceFactory>
export default Environment
11 changes: 9 additions & 2 deletions lib/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import * as url from 'url'
import type { Term } from '@rdfjs/types'
import isURI from 'is-uri'

export function resolveImport(importNode: Term, basePath: string | URL | undefined) {
interface Options {
basePath?: string | URL
extension?: string
}

export function resolveImport(importNode: Term, { basePath, extension }: Options = {}) {
if (importNode.termType !== 'NamedNode') {
throw new Error(`Import target must be a NamedNode, got ${importNode.termType}`)
}
Expand All @@ -12,5 +17,7 @@ export function resolveImport(importNode: Term, basePath: string | URL | undefin
}

const base = typeof basePath === 'string' ? url.pathToFileURL(basePath) : basePath
return url.fileURLToPath(new URL(importNode.value + '.ttl', base))

const filePath = extension ? `${importNode.value}.${extension}` : importNode.value
return url.fileURLToPath(new URL(filePath, base))
}
42 changes: 21 additions & 21 deletions test/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ exports[`rdf-merge-stream imports remote resources from files 1`] = `
"@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix schema: <http://schema.org/> .
<http://example.org/required-property> sh:minCount 1 ;
sh:maxCount 1 .
<> a sh:NodeShape ;
sh:property <http://localhost:6666/property/identifier> .
<http://localhost:6666/property/identifier> sh:minLength 3 ;
<http://localhost:6666/property/identifier> sh:path schema:identifier ;
sh:minLength 3 ;
sh:and (
<http://example.org/required-property>
) ;
sh:path schema:identifier .
) .
<> a sh:NodeShape ;
sh:property <http://localhost:6666/property/identifier> .
<http://example.org/required-property> sh:minCount 1 ;
sh:maxCount 1 .
"
`;
Expand All @@ -23,17 +23,17 @@ exports[`rdf-merge-stream imports remote resources from http 1`] = `
"@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix schema: <http://schema.org/> .
<http://example.org/required-property> sh:minCount 1 ;
sh:maxCount 1 .
<http://localhost:6666/shape> a sh:NodeShape ;
sh:property <http://localhost:6666/property/identifier> .
<http://localhost:6666/property/identifier> sh:minLength 3 ;
<http://localhost:6666/property/identifier> sh:path schema:identifier ;
sh:minLength 3 ;
sh:and (
<http://example.org/required-property>
) ;
sh:path schema:identifier .
) .
<http://localhost:6666/shape> a sh:NodeShape ;
sh:property <http://localhost:6666/property/identifier> .
<http://example.org/required-property> sh:minCount 1 ;
sh:maxCount 1 .
"
`;
Expand All @@ -42,17 +42,17 @@ exports[`rdf-merge-stream merges file stream by relative path 1`] = `
"@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix schema: <http://schema.org/> .
<http://example.com/required-property> sh:minCount 1 ;
sh:maxCount 1 .
<http://example.com/shape> a sh:NodeShape ;
sh:property <http://example.com/shape#property-identifier> .
<http://example.com/shape#property-identifier> sh:minLength 3 ;
<http://example.com/shape#property-identifier> sh:path schema:identifier ;
sh:minLength 3 ;
sh:and (
<http://example.com/required-property>
) ;
sh:path schema:identifier .
) .
<http://example.com/shape> a sh:NodeShape ;
sh:property <http://example.com/shape#property-identifier> .
<http://example.com/required-property> sh:minCount 1 ;
sh:maxCount 1 .
"
`;
1 change: 1 addition & 0 deletions test/resources/import-relative.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ PREFIX sh: <http://www.w3.org/ns/shacl#>

[
code:imports <./property/identifier> ;
code:extension "ttl" ;
] .

shape:
Expand Down
1 change: 1 addition & 0 deletions test/resources/property/identifier.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ prefix code: <https://code.described.at/>

[
code:imports <./shared> ;
code:extension "ttl" ;
] .

property:identifier
Expand Down

0 comments on commit 4cdaf66

Please sign in to comment.