Skip to content

Commit

Permalink
fix(JSON-LD): Improve JSON-LD context generation
Browse files Browse the repository at this point in the history
  • Loading branch information
nokome committed Aug 20, 2019
1 parent 008247f commit af2b8e9
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 46 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"build": "npm run build:jsonschema && npm run build:jsonld && npm run build:ts && npm run build:py && npm run build:r",
"build:jsonschema": "ts-node ts/schema.ts",
"build:jsonld": "ts-node ts/bindings/jsonld.ts",
"build:ts": "ts-node ts/bindings/typescript.ts && tsc && cp built/*.schema.json dist",
"build:ts": "ts-node ts/bindings/typescript.ts && tsc && cp built/*.schema.json dist && cp built/*.jsonld dist",
"build:py": "ts-node ts/bindings/python.ts",
"build:r": "ts-node ts/bindings/r.ts",
"docs": "npm run docs:readme && npm run docs:build",
Expand Down
107 changes: 62 additions & 45 deletions ts/bindings/jsonld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,53 @@
* Generate `built/*.jsonld` files from `schema/*.schema.yaml` files.
*
* For custom types (those not defined elsewhere) generates a JSON-LD
* file similar to e.g. https://schema.org/Person.jsonld
* file similar to those on schema.org e.g. https://schema.org/Person.jsonld
*
* For custom properties generates a JSON-LD file similar to
* e.g. https://schema.org/sibling.jsonld
* those on schema.org e.g. https://schema.org/sibling.jsonld
*/

import fs from 'fs-extra'
import path from 'path'
// @ts-ignore
import fromEntries from 'object.fromentries'
import { read } from './utils'

export const build = async (): Promise<void> => {
await fs.ensureDir(path.join(__dirname, '..', '..', 'built'))

const types: { [key: string]: {} } = {}
const properties: {
[key: string]: { '@id': string } & { [key: string]: unknown }
} = {}

const schemas = await read()
for (const schema of schemas.values()) {
const { '@id': typeId, title, properties: typeProperties } = schema
const {
'@id': typeId,
title,
properties: typeProperties
} = schema

// Skip union types like `Node` and `BlockContent` that do not need to
// be represented here.
// Skip union types, like `Node` and `BlockContent`, that do not need to
// be represented in the `@context`.
if (typeId === undefined || title === undefined || properties === undefined)
continue

// Create a schema.org [`Class`](https://meta.schema.org/Class) for those
// types that are defined by this schema, otherwise link to the class defined
// in the other context.
// Create a schema.org [`Class`](https://meta.schema.org/Class) for
// types defined by this schema.
if (typeId.startsWith('stencila:')) {
types[title] = {
const classs = {
'@id': typeId,
'@type': 'schema:Class',
'schema:name': title,
'schema:description': schema.description
}
} else {
types[title] = {
'@id': typeId
}

}

types[typeId] = { '@id': typeId, name: title }

// Create a [`Property`](https://meta.schema.org/Property) for those
// properties that are defined by this schema, otherwise link to the property
// defined in the other context.
Expand All @@ -51,12 +57,15 @@ export const build = async (): Promise<void> => {
// for an approach to that.
if (typeProperties !== undefined) {
for (const [name, property] of Object.entries(typeProperties)) {
const pid = property['@id']
if (pid === undefined) continue
let pid = property['@id']
// Do not add terms that are aliases with JSON-LD keywords: @id, @type etc
if (pid === undefined || name == 'id' || name === 'type' || name === 'value') continue
// The `schema` property clashes with the schema.org alias. So rename it...
if (pid === 'stencila:schema') pid = 'stencila:scheme'

if (pid.startsWith('stencila:')) {
if (properties[name] === undefined) {
properties[name] = {
properties[pid] = {
'@id': pid,
'@type': 'schema:Property',
'schema:name': name,
Expand All @@ -70,46 +79,54 @@ export const build = async (): Promise<void> => {
}
}
} else {
properties[name] = {
'@id': pid
properties[pid] = {
'@id': pid,
name
}
}
}
}
}

const jsonld = {
'@context': {
// Contexts referred to, including this one
schema: 'https://schema.org/',
bioschemas: 'http://bioschemas.org',
codemeta: 'https://doi.org/10.5063/schema/codemeta-2.0',
stencila: 'https://stencila.github.io/schema/01-draft',
/**
* The main JSON-LD @context.
*
* Written to be similar to schema.org's @context:
* https://schema.org/docs/jsonldcontext.jsonld
*/
const context = {
// Alias JSON-LD keywords e.g. `@type` and `@id`
// For why this is useful, see "Addressing the “@” issue" at
// https://datalanguage.com/news/publishing-json-ld-for-developers
type: '@type',
id: '@id',
value: '@value',

// Alias `@type` and `@id`
// See "Addressing the “@” issue" at https://datalanguage.com/news/publishing-json-ld-for-developers
// for why this is useful.
type: '@type',
id: '@id'
}
}
// Other contexts referred to, including this one
// Note that http vs https is important!
schema: 'http://schema.org/',
bioschemas: 'http://bioschemas.org',
codemeta: 'http://doi.org/10.5063/schema/codemeta-2.0',
stencila: 'http://schema.stenci.la/',

// Add types and properties alphabetically
for (const [key, value] of [
...[...Object.entries(types)].sort(),
...[...Object.entries(properties)].sort()
]) {
// @ts-ignore
jsonld[key] = value
}
// Define that in this context all terms derive from this vocabulary
// (and so do not need prefixing)
"@vocab": "http://schema.stenci.la/",

await fs.ensureDir(path.join(__dirname, '..', '..', 'built'))
// Types and properties added in alphabetical order after this e.g
// "schema:AudioObject": {"@id": "schema:AudioObject"},
...fromEntries([
...[...Object.keys(types)].sort(),
...[...Object.keys(properties)].sort()
].map((id: string) => {
const term = id.split(':')[1]
return [term, { '@id': id }]
}))
}
await fs.writeJSON(
path.join(__dirname, '..', '..', 'built', 'stencila.jsonld'),
jsonld,
{
spaces: 2
}
{ '@context': context },
{ spaces: 2 }
)
}

Expand Down

0 comments on commit af2b8e9

Please sign in to comment.