Skip to content

Commit cfd3b62

Browse files
authored
Merge pull request #28 from w3c/Improve-generated-context
Improve generated context and other improvements
2 parents 26aad65 + 7d9f6d0 commit cfd3b62

File tree

13 files changed

+1240
-762
lines changed

13 files changed

+1240
-762
lines changed

README.md

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@ Each block sequence consists of blocks with the following keys:`id`, `property`,
2424
- The `context` key refers to list of URLs or two special keywords. It is used to add information on JSON-LD `@context` file(s) that "mention" the term; the list of URLs refer to the relevant `@context` file. If the value is `vocab`, and a global `@context` file is defined in the `vocab` block, that "default" `@context` is used. Finally, if the value of the property is `none`, there is no context file reference for the term. The default setting is `vocab` (i.e., unless it is otherwise specified, the default value is used for the term).
2525
- The `example` key refers to on or more blocks with `label` and `json` keys, providing a (JSON) example with a title. These examples are placed, in the HTML version, to the end of the section referring to a term (the examples are ignored in the Turtle and the JSON-LD versions). Care should be taken to use the `"|"` [block style indicator](https://yaml-multiline.info) in the YAML file for the examples.
2626

27-
2827
- Top level blocks:
29-
- `vocab`: a block with the `id` and the `value` keys defining the prefix and the URL of the vocabulary, respectively. The `id` provides a prefix that can be used in the vocabulary descriptions, e.g., for cross-references. The additional, optional `context` key may provide a default context file reference (as a URI), used by all terms unless locally overwritten (see above).
28+
- `vocab`: a block with the `id` and the `value` keys defining the prefix and the URL of the vocabulary, respectively. The `id` provides a prefix that can be used in the vocabulary descriptions, e.g., for cross-references. The additional, optional `context` key may provide a default context file reference (as a URI), used by all terms unless locally overwritten (see above). Note that the `context` key is required if the HTML template includes a context section.
3029

3130
- `prefix`: definition of a prefixes, and corresponding URLs, for each external vocabulary in use, defined by the `id` and `value` keys, respectively.
3231

@@ -64,7 +63,6 @@ External terms, while they appear in the HTML document generated by the tool, do
6463

6564
The prefix part of the curie _must_ be defined through the `prefix` top level block.
6665

67-
6866
## Installation and use
6967

7068
The script is in TypeScript (version 5.0.2 and beyond) running on top of [`node.js`](https://nodejs.org) (version 21 and beyond). It can also run with [`deno`](http://deno.land) (version 2.1 and beyond).
@@ -82,15 +80,35 @@ npm install yml2vocab
8280

8381
#### Running on a command line
8482

83+
##### NPM/Node.js
84+
8585
The npm installation installs the `node_modules/.bin/yml2vocab` script. The script can be used as:
8686

8787
```
8888
yml2vocab [-v vocab_file_name] [-t template_file_name] [-c]
8989
```
9090

91-
Running this script generates the `vocab_file_name.ttl`, `vocab_file_name.jsonld`, and `vocab_file_name.html` files for the Turtle, JSON-LD, and HTML+RDFa versions, respectively. The script relies on the `vocab_file_name.yml` file for the vocabulary specification in YAML and a `template_file_name` file for a template file. The defaults are `vocabulary` and `template.html`, respectively.
91+
##### Deno
92+
93+
If `deno` is installed globally, one can also run the script directly (without any further installation) by
94+
95+
```
96+
deno run --allow-read --allow-write --allow-env /a/b/c/main_.ts [-v vocab_file_name] [-t template_file_name] [-c]
97+
```
98+
99+
in the top level. To make it simpler, a binary, compiled version of the program can be generated by
92100

93-
If the `-c` flag is also set, the additional `vocab_file_name_context.jsonld` is also generated, containing a simple `@context` structure that can be used as a separate `@context` file or embedded in a JSON file. Note that this is a "minimal" JSON-LD file, which does not necessarily use all the sophistication that JSON-LD [defines](https://www.w3.org/TR/json-ld11/#the-context) for `@context`; these may have to be added manually.
101+
```
102+
deno compile --allow-read --allow-write --allow-env main.ts
103+
```
104+
105+
which results in an executable file, called `yml2vocab`, that can be stored anywhere in the user's `$PATH`.
106+
107+
#### Command line argument
108+
109+
The script generates the `vocab_file_name.ttl`, `vocab_file_name.jsonld`, and `vocab_file_name.html` files for the Turtle, JSON-LD, and HTML+RDFa versions, respectively. The script relies on the `vocab_file_name.yml` file for the vocabulary specification in YAML and a `template_file_name` file for a template file. The defaults are `vocabulary` and `template.html`, respectively.
110+
111+
If the `-c` flag is also set, the additional `vocab_file_name.context.jsonld` is also generated, containing a JSON-LD file that can be used as a separate `@context` reference in a JSON-LD file. Note that this JSON-LD file does not necessarily use all the sophistication that JSON-LD [defines](https://www.w3.org/TR/json-ld11/#the-context) for `@context`; these may have to be added manually.
94112

95113
#### Running from a Javascript/TypeScript program
96114

@@ -128,26 +146,7 @@ There is no need to install any extra typing, it is included in the package. The
128146

129147
### Cloning the repository
130148

131-
The [repository](https://github.com/yml2vocab) may also be cloned. For a complete installation:
132-
133-
1. If necessary, install [`node.js`](https://nodejs.org/) on your local machine. Installation of `node.js` should automatically install the [`npm`](https://www.npmjs.com) package manager.
134-
2. Clone the repository (i.e., https://github.com/w3c/yml2vocab/) to your local machine.
135-
3. In the directory of the repository clone, run `npm install` on the command line. This installs all the necessary packages in the `node_modules` subdirectory.
136-
4. Create a directory for the vocabulary definition; this should include
137-
1. A `vocabulary.yml` file. You can start with the YAML file in the `example` directory of the repository, and change the cells for your vocabulary.
138-
2. A `template.html` file. You can start with the HTML file in the `example` directory of the repository, and adapt/change it as you wish.
139-
5. Run the `main.ts` file in the directory vocabulary definition. This generates the `vocabulary.ttl`, `vocabulary.jsonld`, and `vocabulary.html` files for, respectively, the Turtle, JSON-LD, and HTML representations.
140-
141-
"Running" may be done in three different ways:
142-
143-
1. Run, via `node`, the file `dist/main.js` of the repository
144-
2. Run, via `node_modules/.bin/ts-node`, the file `main.ts` of the repository
145-
3. Run, via `deno run`, the file `main.ts` or the repository
146-
4. Alternatively, an executable file can be generated via `deno task compile` in the repository, which can then be put to the user's `$PATH`.
147-
148-
Alternative (2) or (3) is preferred
149-
150-
The script also accepts a single argument to be used instead of `vocabulary` to name the various files (see above).
149+
The [repository](https://github.com/yml2vocab) may also be cloned.
151150

152151
#### Content of the directory
153152

@@ -169,6 +168,6 @@ The following files and directories are generated/modified by either the script
169168

170169
## Acknowledgement
171170

172-
The original idea, structure, and script (in Ruby) was created by Gregg Kellogg for v1 of the Credentials Vocabulary and with a vocabulary definition using CSV. The CSV definitions have been changed to YAML, and the script itself has been re-written in TypeScript (and developed further since).
171+
The original idea, structure, and script (in Ruby) was created by Gregg Kellogg for v1 of the Credentials Vocabulary and with a vocabulary definition using CSV. The CSV definitions have been changed to YAML, and the script itself has been re-written in TypeScript, and developed further since.
173172

174173
Many features are the result of further discussions with Many Sporny and Benjamin Young.

deno.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "1.5.0",
2+
"version": "1.5.4",
33
"description": "Generation of vocabulary files starting by YAML",
44
"homepage": "https://github.com/w3c/yml2vocab",
55
"nodeModulesDir": "none",
@@ -15,6 +15,7 @@
1515
"test_di": "cd test/di; deno run --allow-read --allow-write --allow-env ../../main.ts -c",
1616
"test_vcdm": "cd test/vcdm; deno run --allow-read --allow-write --allow-env ../../main.ts -c",
1717
"local_vcdm_nolink": "cd local/tests/vcdm; deno run --allow-read --allow-write --allow-env ../../../main.ts -c",
18+
"local_ident": "cd local/tests/identification; deno run --allow-read --allow-write --allow-env ../../../main.ts -c",
1819
"compile": "deno compile --allow-read --allow-write --allow-env main.ts"
1920
},
2021
"main": "index.ts",

index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export async function generateVocabularyFiles(yaml_file_name: string, template_f
131131
fs.writeFile(`${basename}.html`, conversion.getHTML(template)),
132132
];
133133
if (context) {
134-
fs_writes.push(fs.writeFile(`${basename}_context.jsonld`, conversion.getContext()))
134+
fs_writes.push(fs.writeFile(`${basename}.context.jsonld`, conversion.getContext()))
135135
}
136136
const write_errors = (await Promise.allSettled(fs_writes))
137137
.filter((result): boolean => result.status === "rejected")

lib/context.ts

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
* @packageDocumentation
66
*/
77

8-
import { Vocab, global, RDFProperty } from './common';
8+
import { Vocab, global, RDFProperty, RDFTerm } from './common';
99

1010
// Just to get an extra help from TS if I mistype something...
1111
interface Context {
12-
[index: string]: string|Context|boolean;
12+
[index: string]: string|Context|boolean|null;
1313
}
1414

1515
// These are the context statements appearing in all
@@ -20,6 +20,20 @@ const preamble: Context = {
2020
"type": "@type",
2121
};
2222

23+
// Minor utility: return the full URL for a prefix
24+
function prefix_url(prefix: string | undefined, vocab: Vocab): string {
25+
if (!prefix) {
26+
// This never happens per the program logic, but we keep TS happy...
27+
return global.vocab_url;
28+
}
29+
for (const pr of vocab.prefixes) {
30+
if (pr.prefix === prefix) {
31+
return pr.url;
32+
}
33+
}
34+
return global.vocab_url;
35+
}
36+
2337
/**
2438
* Generate the minimal JSON-LD context for the vocabulary.
2539
*
@@ -30,27 +44,43 @@ export function toContext(vocab: Vocab): string {
3044
// Generation of a unit for properties
3145
const propertyContext = (property: RDFProperty, forClass = true): Context|string => {
3246
// the real id of the property...
33-
const url = `${global.vocab_url}${property.id}`;
47+
const baseUrl = prefix_url(property.prefix, vocab);
48+
const url = `${baseUrl}${property.id}`;
3449
const output: Context = {
3550
"@id" : url
3651
}
37-
if (forClass || property.type.includes("owl:ObjectProperty")) {
52+
if (forClass && property.type.includes("owl:ObjectProperty")) {
3853
output["@type"] = "@id";
3954
}
4055
// Try to catch the datatype settings; these can be used
4156
// to set these in the context as well
4257
if (property.range) {
4358
for (const range of property.range) {
4459
if (range.startsWith("xsd:")) {
45-
output["@type"] = range.replace('xsd:', 'http://www.w3.org/2001/XMLSchema#');
60+
output["@type"] = range.replace("xsd:", "http://www.w3.org/2001/XMLSchema#");
61+
break;
62+
} else if (range === "rdf:JSON") {
63+
output["@type"] = "@json";
64+
break;
65+
} else if (["rdf:HTML", "rdf:XMLLiteral", "rdf:PlainLiteral", "rdf:langString"].includes(range)) {
66+
output["@type"] = range.replace("rdf:", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
4667
break;
4768
} else if (range === "rdf:List") {
4869
output["@container"] = "@list";
70+
break;
71+
} else if (property.type.includes("owl:DatatypeProperty")) {
72+
// This is the case when the property refers to an explicitly defined, non-standard datatype
73+
const [range_prefix, range_reference] = range.split(":");
74+
const range_url = prefix_url(range_prefix, vocab)
75+
output["@type"] = range_url + range_reference;
76+
break;
4977
}
78+
5079
}
5180
}
5281
if (property.dataset) {
5382
output["@container"] = "@graph";
83+
output["@type"] = "@id";
5484
}
5585

5686
// if only the URL is set, it makes the context simpler to use its direct value,

lib/convert.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ function finalizeRawEntry(raw: RawVocabEntry): RawVocabEntry {
256256
comment : (raw.comment) ? cleanComment(raw.comment) : "",
257257
see_also : toSeeAlso(raw.see_also),
258258
example : toExample(raw.example),
259-
dataset : (raw.dataset === undefined) ? false : raw.dataset,
259+
dataset : raw.dataset ?? false,
260260
context : toArrayContexts(raw.context)
261261
}
262262
}
@@ -377,14 +377,15 @@ export function getData(vocab_source: string): Vocab {
377377
return terms.length === 1 ? range : terms[1];
378378
});
379379
if (pure_refs.length !== 0 && pure_refs.indexOf(raw.id) !== -1) {
380-
(pure_refs.length === 1 ? single_ref : multi_ref).push(property.id);
380+
const id_to_store = (global.vocab_prefix !== property.prefix) ? `${property.prefix}:${property.id}` : property.id;
381+
(pure_refs.length === 1 ? single_ref : multi_ref).push(id_to_store);
381382
return true;
382383
}
383384
}
384385
return false;
385386
}
386387

387-
// Handling external terms, that are characterized by the fact that they are identified as a CURIE in
388+
// Handling external terms. These are characterized by the fact that they are identified as a CURIE in
388389
// the YAML file. The prefix and the term must be separated.
389390
// To make the handling of all this uniform, the core term also get the (default) prefix stored in their
390391
// structure.
@@ -483,7 +484,10 @@ export function getData(vocab_source: string): Vocab {
483484
];
484485

485486
// Get the properties. Note the special treatment for deprecated properties, as well as
486-
// the extra owl types added depending on the range
487+
// the extra owl types added depending on the range.
488+
//
489+
// Note that the function sets the object property or datatype property types in the obvious cases.
490+
// These extra types are also added when handling a class or a datatype, looking up the references.
487491
const properties: RDFProperty[] = (vocab.property !== undefined) ?
488492
vocab.property.map((raw: RawVocabEntry): RDFProperty => {
489493
const {prefix, id, external} = check_id(raw);
@@ -555,7 +559,12 @@ export function getData(vocab_source: string): Vocab {
555559

556560
// Get all domain/range cross-references
557561
for (const property of properties) {
558-
crossref(raw, property, property.range, range_of, includes_range_of);
562+
const is_obj_property = crossref(raw, property, property.range, range_of, includes_range_of);
563+
// the relevant property should be marked as an object property!
564+
if (is_obj_property) {
565+
// a bit convoluted, but trying to avoid repeating the extra entry
566+
property.type = [...((new Set(property.type)).add('owl:ObjectProperty'))];
567+
}
559568
crossref(raw, property, property.domain, domain_of, included_in_domain_of);
560569
}
561570

@@ -573,7 +582,7 @@ export function getData(vocab_source: string): Vocab {
573582
subClassOf : raw.upper_value,
574583
see_also : raw.see_also,
575584
example : raw.example,
576-
context : final_contexts(raw, `${prefix}:${id}`),
585+
context : final_contexts(raw, `${prefix}:${id}`),
577586
range_of, domain_of, included_in_domain_of, includes_range_of
578587
}
579588
}) : [];

0 commit comments

Comments
 (0)