@faubulous/mentor-rdf-serializers 1.1.4 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -531
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/statement-group.d.ts +126 -0
- package/dist/statement-group.d.ts.map +1 -0
- package/dist/statement-group.js +271 -0
- package/dist/statement-group.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,82 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
A comprehensive RDF serialization library for TypeScript, designed as the counterpart to [@faubulous/mentor-rdf-parsers](https://github.com/faubulous/mentor-rdf-parsers). Optimized for IDE use cases such as code formatting and partial serialization.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Multiple Formats**: N-Triples, N-Quads, Turtle, TriG, N3, JSON-LD
|
|
8
|
-
- **SPARQL Formatting**: Format SPARQL queries and updates with comment preservation
|
|
9
|
-
- **RDF 1.2 Support**: Full support for RDF 1.2 features including:
|
|
10
|
-
- Triple Terms (`<<( s p o )>>`)
|
|
11
|
-
- Reified Triples (`<< s p o >>`)
|
|
12
|
-
- Annotations (`{| p o |}`)
|
|
13
|
-
- Language direction (`@en--ltr`, `@ar--rtl`)
|
|
14
|
-
- **Advanced Formatting Options**: Control line width, alignment, blank node style, and more
|
|
15
|
-
- **Comment Preservation**: Preserve comments when reformatting source files
|
|
16
|
-
- **Token-based Serialization**: Serialize directly from parser tokens, preserving blank node IDs
|
|
17
|
-
- **Sorting Strategies**: Pluggable sorting with built-in alphabetical and priority-based strategies
|
|
18
|
-
- **IDE Integration**: Designed for code formatting and partial document serialization
|
|
19
|
-
- **Prefix Handling**: Automatic prefix compaction and expansion
|
|
20
|
-
- **Configurable Output**: Pretty printing, sorting, grouping options
|
|
21
|
-
|
|
22
|
-
## Architecture
|
|
1
|
+
<div align="center">
|
|
23
2
|
|
|
24
|
-
|
|
3
|
+
# @faubulous/mentor-rdf-serializers
|
|
25
4
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
B[SPARQL text]
|
|
31
|
-
C[RDF/JS quads]
|
|
32
|
-
end
|
|
5
|
+
[](https://www.npmjs.com/package/@faubulous/mentor-rdf-serializers)
|
|
6
|
+
[](https://github.com/faubulous/mentor-rdf-serializers/blob/main/LICENSE)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://www.w3.org/TR/rdf12-concepts/)
|
|
33
9
|
|
|
34
|
-
|
|
35
|
-
P1[Parser]
|
|
36
|
-
T1[Tokens]
|
|
37
|
-
Q1[Quads]
|
|
10
|
+
**A TypeScript library for serializing and formatting RDF and SPARQL, built for IDE integration.**
|
|
38
11
|
|
|
39
|
-
|
|
40
|
-
P1 --> T1
|
|
41
|
-
P1 --> Q1
|
|
12
|
+
</div>
|
|
42
13
|
|
|
43
|
-
|
|
44
|
-
end
|
|
14
|
+
---
|
|
45
15
|
|
|
46
|
-
|
|
47
|
-
S1[Quad serializers\n(Turtle, N-Triples, ...)]
|
|
48
|
-
TS[TokenSerializer]
|
|
49
|
-
SF[SparqlFormatter]
|
|
50
|
-
end
|
|
16
|
+
Designed as the counterpart to [@faubulous/mentor-rdf-parsers](https://github.com/faubulous/mentor-rdf-parsers), this library provides RDF serialization and SPARQL formatting with first-class support for editor workflows such as document formatting, range formatting, comment preservation, and configurable output styles.
|
|
51
17
|
|
|
52
|
-
|
|
53
|
-
O1[Serialized / formatted RDF]
|
|
54
|
-
O2[Formatted SPARQL]
|
|
55
|
-
end
|
|
18
|
+
## Features
|
|
56
19
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
20
|
+
| | |
|
|
21
|
+
|---|---|
|
|
22
|
+
| **Multi-Format Output** | Serialize to N-Triples, N-Quads, Turtle, TriG, N3, and JSON-LD |
|
|
23
|
+
| **SPARQL Formatting** | Format queries and updates with keyword casing, alignment, and comment preservation |
|
|
24
|
+
| **RDF 1.2** | Triple terms (`<<( s p o )>>`), reified triples, annotations, and language direction |
|
|
25
|
+
| **Sorting Strategies** | Alphabetical, by-type, semantic, and priority-based quad ordering |
|
|
26
|
+
| **Comment Preservation** | Token-based serialization keeps comments attached to their statements |
|
|
27
|
+
| **Token Serialization** | Serialize directly from parser tokens to preserve blank node IDs and source layout |
|
|
28
|
+
| **Configurable Layout** | Line width, predicate alignment, blank node style, object list wrapping, and more |
|
|
29
|
+
| **IDE-Ready** | Partial document serialization, range formatting, and VS Code integration examples |
|
|
61
30
|
|
|
62
|
-
|
|
63
|
-
style mentor-rdf-serializers fill:#fff3e0
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Installation
|
|
31
|
+
## Quick Start
|
|
67
32
|
|
|
68
33
|
```bash
|
|
69
34
|
npm install @faubulous/mentor-rdf-serializers
|
|
70
35
|
```
|
|
71
36
|
|
|
72
|
-
## Quick Start
|
|
73
|
-
|
|
74
37
|
```typescript
|
|
75
38
|
import { TurtleSerializer } from '@faubulous/mentor-rdf-serializers';
|
|
76
39
|
import DataFactory from '@rdfjs/data-model';
|
|
77
40
|
|
|
78
41
|
const serializer = new TurtleSerializer();
|
|
79
|
-
|
|
80
42
|
const quads = [
|
|
81
43
|
DataFactory.quad(
|
|
82
44
|
DataFactory.namedNode('http://example.org/subject'),
|
|
@@ -90,493 +52,50 @@ const quads = [
|
|
|
90
52
|
)
|
|
91
53
|
];
|
|
92
54
|
|
|
93
|
-
|
|
55
|
+
console.log(serializer.serialize(quads, {
|
|
94
56
|
prefixes: {
|
|
95
57
|
'ex': 'http://example.org/',
|
|
96
58
|
'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
|
|
97
59
|
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
console.log(turtle);
|
|
101
|
-
// @prefix ex: <http://example.org/> .
|
|
102
|
-
// @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
103
|
-
//
|
|
104
|
-
// ex:subject a ex:Class ;
|
|
105
|
-
// ex:name "Example"@en .
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Serializers
|
|
109
|
-
|
|
110
|
-
### N-Triples
|
|
111
|
-
|
|
112
|
-
```typescript
|
|
113
|
-
import { NTriplesSerializer } from '@faubulous/mentor-rdf-serializers';
|
|
114
|
-
|
|
115
|
-
const serializer = new NTriplesSerializer();
|
|
116
|
-
const output = serializer.serialize(quads, { sort: true });
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### N-Quads
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
import { NQuadsSerializer } from '@faubulous/mentor-rdf-serializers';
|
|
123
|
-
|
|
124
|
-
const serializer = new NQuadsSerializer();
|
|
125
|
-
const output = serializer.serialize(quads);
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### Turtle
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
import { TurtleSerializer } from '@faubulous/mentor-rdf-serializers';
|
|
132
|
-
|
|
133
|
-
const serializer = new TurtleSerializer();
|
|
134
|
-
const output = serializer.serialize(quads, {
|
|
135
|
-
prefixes: { 'ex': 'http://example.org/' },
|
|
136
|
-
baseIri: 'http://example.org/',
|
|
137
|
-
groupBySubject: true,
|
|
138
|
-
sort: true
|
|
139
|
-
});
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### TriG
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
import { TriGSerializer } from '@faubulous/mentor-rdf-serializers';
|
|
146
|
-
|
|
147
|
-
const serializer = new TriGSerializer();
|
|
148
|
-
const output = serializer.serialize(quadsWithGraphs, {
|
|
149
|
-
prefixes: { 'ex': 'http://example.org/' }
|
|
150
|
-
});
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### N3
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
import { N3Serializer } from '@faubulous/mentor-rdf-serializers';
|
|
157
|
-
|
|
158
|
-
const serializer = new N3Serializer();
|
|
159
|
-
const output = serializer.serialize(quads); // Supports formulas and implications
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### JSON-LD
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
import { JsonLdSerializer } from '@faubulous/mentor-rdf-serializers';
|
|
166
|
-
|
|
167
|
-
const serializer = new JsonLdSerializer();
|
|
168
|
-
const output = serializer.serialize(quads, {
|
|
169
|
-
prefixes: { 'ex': 'http://example.org/' },
|
|
170
|
-
useTypeShorthand: true,
|
|
171
|
-
useIdShorthand: true,
|
|
172
|
-
compactArrays: true,
|
|
173
|
-
prettyPrint: true
|
|
174
|
-
});
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## SPARQL Formatting
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
import { SparqlFormatter } from '@faubulous/mentor-rdf-serializers';
|
|
181
|
-
|
|
182
|
-
const formatter = new SparqlFormatter();
|
|
183
|
-
|
|
184
|
-
// Format a SELECT query
|
|
185
|
-
const selectResult = formatter.formatFromText(`
|
|
186
|
-
SELECT ?s ?p ?o WHERE { ?s ?p ?o . FILTER(?o > 10) }
|
|
187
|
-
`, {
|
|
188
|
-
uppercaseKeywords: true,
|
|
189
|
-
alignPatterns: true,
|
|
190
|
-
sameBraceLine: true,
|
|
191
|
-
separateClauses: true,
|
|
192
|
-
maxLineWidth: 80
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
console.log(selectResult.output);
|
|
196
|
-
|
|
197
|
-
// Format a SPARQL UPDATE (same API)
|
|
198
|
-
const updateResult = formatter.formatFromText(`
|
|
199
|
-
INSERT DATA { <http://example.org/s> <http://example.org/p> "value" }
|
|
200
|
-
`, {
|
|
201
|
-
indent: ' ',
|
|
202
|
-
uppercaseKeywords: true
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
console.log(updateResult.output);
|
|
206
|
-
|
|
207
|
-
// Comments are preserved
|
|
208
|
-
const withComments = formatter.formatFromText(`
|
|
209
|
-
# Find all people
|
|
210
|
-
SELECT ?person WHERE {
|
|
211
|
-
?person a <http://example.org/Person> . # Match persons
|
|
212
|
-
}
|
|
213
|
-
`);
|
|
214
|
-
|
|
215
|
-
console.log(withComments.output); // Comments remain in the formatted output
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
## Token-based Serialization
|
|
219
|
-
|
|
220
|
-
For IDE integration, you can serialize directly from parser tokens, which preserves blank node IDs and comments from the source document.
|
|
221
|
-
|
|
222
|
-
```typescript
|
|
223
|
-
import { TokenSerializer } from '@faubulous/mentor-rdf-serializers';
|
|
224
|
-
import { TurtleParser } from '@faubulous/mentor-rdf-parsers';
|
|
225
|
-
|
|
226
|
-
const parser = new TurtleParser();
|
|
227
|
-
const parseResult = parser.parse(sourceDocument);
|
|
228
|
-
|
|
229
|
-
const tokenSerializer = new TokenSerializer();
|
|
230
|
-
|
|
231
|
-
// Serialize all tokens
|
|
232
|
-
const output = tokenSerializer.serialize(parseResult.tokens, {
|
|
233
|
-
preserveBlankNodeIds: true,
|
|
234
|
-
preserveComments: true // Keeps comments in output
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// Serialize only a specific range (for range formatting)
|
|
238
|
-
const rangeOutput = tokenSerializer.serializeRange(
|
|
239
|
-
parseResult.tokens,
|
|
240
|
-
startOffset,
|
|
241
|
-
endOffset,
|
|
242
|
-
{ preserveComments: true }
|
|
243
|
-
);
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
## Serialization Options
|
|
247
|
-
|
|
248
|
-
### Common Options
|
|
249
|
-
|
|
250
|
-
| Option | Type | Description |
|
|
251
|
-
|--------|------|-------------|
|
|
252
|
-
| `prefixes` | `Record<string, string>` | Prefix mappings for compacting IRIs |
|
|
253
|
-
| `baseIri` | `string` | Base IRI for relative references |
|
|
254
|
-
| `sort` | `boolean` | Sort output alphabetically |
|
|
255
|
-
| `prettyPrint` | `boolean` | Enable pretty formatting |
|
|
256
|
-
|
|
257
|
-
### Turtle/TriG Options
|
|
258
|
-
|
|
259
|
-
| Option | Type | Default | Description |
|
|
260
|
-
|--------|------|---------|-------------|
|
|
261
|
-
| `groupBySubject` | `boolean` | `true` | Group triples by subject |
|
|
262
|
-
| `usePrefixedNames` | `boolean` | `true` | Use prefixed names where possible |
|
|
263
|
-
| `useShorthandTypes` | `boolean` | `true` | Use `a` for `rdf:type` |
|
|
264
|
-
| `useLongStrings` | `boolean` | `true` | Use `"""` for multiline strings |
|
|
265
|
-
| `useNumericShorthands` | `boolean` | `true` | Use numeric literals without quotes |
|
|
266
|
-
|
|
267
|
-
### Formatting Options
|
|
268
|
-
|
|
269
|
-
These options control the visual layout of Turtle, TriG, and SPARQL output:
|
|
270
|
-
|
|
271
|
-
| Option | Type | Default | Description |
|
|
272
|
-
|--------|------|---------|-------------|
|
|
273
|
-
| `maxLineWidth` | `number` | `0` | Maximum line width before wrapping (0 = no wrapping) |
|
|
274
|
-
| `alignPredicates` | `boolean` | `false` | Align predicates in columns |
|
|
275
|
-
| `alignObjects` | `boolean` | `false` | Align objects in columns (requires `alignPredicates`) |
|
|
276
|
-
| `blankNodeStyle` | `'labeled' \| 'inline' \| 'auto'` | `'auto'` | How to format blank nodes |
|
|
277
|
-
| `objectListStyle` | `'single-line' \| 'multi-line' \| 'auto'` | `'auto'` | How to format multiple objects |
|
|
278
|
-
| `predicateListStyle` | `'single-line' \| 'multi-line' \| 'first-same-line'` | `'first-same-line'` | How to format predicate lists |
|
|
279
|
-
| `blankLinesBetweenSubjects` | `boolean` | `true` | Insert blank lines between subject blocks |
|
|
280
|
-
|
|
281
|
-
#### Blank Node Style
|
|
282
|
-
|
|
283
|
-
```typescript
|
|
284
|
-
// 'labeled' - Always use labeled blank nodes
|
|
285
|
-
_:b0 ex:name "Alice" .
|
|
286
|
-
_:b0 ex:knows _:b1 .
|
|
287
|
-
|
|
288
|
-
// 'inline' - Use inline property lists where possible
|
|
289
|
-
[ ex:name "Alice" ; ex:knows [ ex:name "Bob" ] ] .
|
|
290
|
-
|
|
291
|
-
// 'auto' - Inline for single-reference blank nodes, labeled for multi-reference
|
|
60
|
+
}));
|
|
292
61
|
```
|
|
293
62
|
|
|
294
|
-
#### Object List Style
|
|
295
|
-
|
|
296
|
-
```typescript
|
|
297
|
-
// 'single-line' - Objects on same line
|
|
298
|
-
ex:s ex:p "a", "b", "c" .
|
|
299
|
-
|
|
300
|
-
// 'multi-line' - Each object on its own line
|
|
301
|
-
ex:s ex:p "a",
|
|
302
|
-
"b",
|
|
303
|
-
"c" .
|
|
304
|
-
|
|
305
|
-
// 'auto' - Based on maxLineWidth
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
#### Predicate List Style
|
|
309
|
-
|
|
310
|
-
```typescript
|
|
311
|
-
// 'first-same-line' - First predicate with subject
|
|
312
|
-
ex:s a ex:Class ;
|
|
313
|
-
ex:name "Example" ;
|
|
314
|
-
ex:value 42 .
|
|
315
|
-
|
|
316
|
-
// 'multi-line' - All predicates on separate lines
|
|
317
|
-
ex:s
|
|
318
|
-
a ex:Class ;
|
|
319
|
-
ex:name "Example" ;
|
|
320
|
-
ex:value 42 .
|
|
321
|
-
|
|
322
|
-
// 'single-line' - All on one line (compact)
|
|
323
|
-
ex:s a ex:Class ; ex:name "Example" ; ex:value 42 .
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### JSON-LD Options
|
|
327
|
-
|
|
328
|
-
| Option | Type | Default | Description |
|
|
329
|
-
|--------|------|---------|-------------|
|
|
330
|
-
| `useTypeShorthand` | `boolean` | `true` | Use `@type` instead of `rdf:type` |
|
|
331
|
-
| `useIdShorthand` | `boolean` | `true` | Use `@id` for IRI references |
|
|
332
|
-
| `compactArrays` | `boolean` | `true` | Unwrap single-element arrays |
|
|
333
|
-
| `embedContext` | `boolean` | `true` | Include `@context` in output |
|
|
334
|
-
| `jsonIndent` | `number` | `2` | Indentation for pretty printing |
|
|
335
|
-
|
|
336
|
-
### SPARQL Options
|
|
337
|
-
|
|
338
|
-
| Option | Type | Default | Description |
|
|
339
|
-
|--------|------|---------|-------------|
|
|
340
|
-
| `uppercaseKeywords` | `boolean` | `true` | Uppercase SPARQL keywords |
|
|
341
|
-
| `lowercaseKeywords` | `boolean` | `false` | Lowercase SPARQL keywords |
|
|
342
|
-
| `alignPatterns` | `boolean` | `true` | Align WHERE clause patterns |
|
|
343
|
-
| `sameBraceLine` | `boolean` | `true` | Opening braces on same line |
|
|
344
|
-
| `separateClauses` | `boolean` | `true` | Blank lines between major clauses |
|
|
345
|
-
| `maxLineWidth` | `number` | `0` | Maximum line width before wrapping |
|
|
346
|
-
| `objectListStyle` | `string` | `'auto'` | How to format object lists in patterns |
|
|
347
|
-
| `predicateListStyle` | `string` | `'first-same-line'` | How to format predicate lists |
|
|
348
|
-
| `blankLinesBetweenSubjects` | `boolean` | `true` | Blank lines between subject blocks |
|
|
349
|
-
|
|
350
|
-
### Token Serialization Options
|
|
351
|
-
|
|
352
|
-
| Option | Type | Default | Description |
|
|
353
|
-
|--------|------|---------|-------------|
|
|
354
|
-
| `preserveBlankNodeIds` | `boolean` | `true` | Preserve original blank node IDs |
|
|
355
|
-
| `preserveComments` | `boolean` | `true` | Preserve comments from source |
|
|
356
|
-
|
|
357
|
-
## RDF 1.2 Features
|
|
358
|
-
|
|
359
|
-
### Triple Terms
|
|
360
|
-
|
|
361
|
-
```typescript
|
|
362
|
-
import { TurtleSerializer } from '@faubulous/mentor-rdf-serializers';
|
|
363
|
-
import type { TripleTerm, Rdf12Quad } from '@faubulous/mentor-rdf-serializers';
|
|
364
|
-
|
|
365
|
-
const tripleTerm: TripleTerm = {
|
|
366
|
-
termType: 'TripleTerm',
|
|
367
|
-
subject: { termType: 'NamedNode', value: 'http://example.org/s' },
|
|
368
|
-
predicate: { termType: 'NamedNode', value: 'http://example.org/p' },
|
|
369
|
-
object: { termType: 'Literal', value: 'value', language: '', datatype: { termType: 'NamedNode', value: 'http://www.w3.org/2001/XMLSchema#string' } }
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
// Output: <<( <http://example.org/s> <http://example.org/p> "value" )>>
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
### Reified Triples with Annotations
|
|
376
|
-
|
|
377
63
|
```turtle
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
# Serialized correctly with annotations
|
|
382
|
-
```
|
|
64
|
+
@prefix ex: <http://example.org/> .
|
|
65
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
383
66
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
```typescript
|
|
387
|
-
const literal = DataFactory.literal('مرحبا', 'ar--rtl');
|
|
388
|
-
// Output in Turtle: "مرحبا"@ar--rtl
|
|
389
|
-
// Output in JSON-LD: { "@value": "مرحبا", "@language": "ar", "@direction": "rtl" }
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
## API Reference
|
|
393
|
-
|
|
394
|
-
### Interfaces
|
|
395
|
-
|
|
396
|
-
#### `ISerializer`
|
|
397
|
-
|
|
398
|
-
```typescript
|
|
399
|
-
interface ISerializer {
|
|
400
|
-
serializeQuad(quad: Quad | Rdf12Quad, options?: SerializerOptions): string;
|
|
401
|
-
serialize(quads: Iterable<Quad | Rdf12Quad>, options?: SerializerOptions): string;
|
|
402
|
-
format(quads: Iterable<Quad | Rdf12Quad>, options?: SerializerOptions): SerializationResult;
|
|
403
|
-
}
|
|
67
|
+
ex:subject a ex:Class ;
|
|
68
|
+
ex:name "Example"@en .
|
|
404
69
|
```
|
|
405
70
|
|
|
406
|
-
|
|
71
|
+
## Documentation
|
|
407
72
|
|
|
408
|
-
|
|
409
|
-
interface SerializationResult {
|
|
410
|
-
output: string;
|
|
411
|
-
warnings?: string[];
|
|
412
|
-
sourceMap?: SourceMapEntry[];
|
|
413
|
-
}
|
|
414
|
-
```
|
|
73
|
+
Detailed guides and API documentation are available in the [docs](docs/README.md) folder:
|
|
415
74
|
|
|
416
|
-
|
|
75
|
+
| Guide | Description |
|
|
76
|
+
|-------|-------------|
|
|
77
|
+
| [Getting Started](docs/getting-started.md) | Installation and first serialization |
|
|
78
|
+
| [Architecture](docs/architecture.md) | Parser/serializer pipeline overview |
|
|
79
|
+
| [Serializers](docs/serializers.md) | Format-specific serializer usage |
|
|
80
|
+
| [Token Serialization](docs/token-serialization.md) | Serialize from parser tokens |
|
|
81
|
+
| [SPARQL Formatting](docs/sparql-formatting.md) | Query and update formatting |
|
|
82
|
+
| [Sorting](docs/sorting.md) | Quad ordering strategies |
|
|
83
|
+
| [Options](docs/options.md) | Full configuration reference |
|
|
84
|
+
| [RDF 1.2](docs/rdf-1.2.md) | Triple terms, annotations, language direction |
|
|
85
|
+
| [API Reference](docs/api-reference.md) | Interfaces and types |
|
|
86
|
+
| [VS Code Formatter](docs/vs-code-formatter.md) | Editor integration example |
|
|
417
87
|
|
|
418
|
-
|
|
419
|
-
interface ISparqlFormatter {
|
|
420
|
-
formatFromText(query: string, options?: SerializerOptions): SerializationResult;
|
|
421
|
-
formatFromTokens(tokens: unknown[], options?: SerializerOptions): SerializationResult;
|
|
422
|
-
}
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
## VS Code Formatter
|
|
426
|
-
|
|
427
|
-
Here's how to create a document formatter for Turtle files in VS Code:
|
|
428
|
-
|
|
429
|
-
```typescript
|
|
430
|
-
import * as vscode from 'vscode';
|
|
431
|
-
import { TurtleParser } from '@faubulous/mentor-rdf-parsers';
|
|
432
|
-
import { TurtleSerializer } from '@faubulous/mentor-rdf-serializers';
|
|
433
|
-
|
|
434
|
-
export class TurtleDocumentFormatter implements vscode.DocumentFormattingEditProvider {
|
|
435
|
-
private parser = new TurtleParser();
|
|
436
|
-
private serializer = new TurtleSerializer();
|
|
437
|
-
|
|
438
|
-
provideDocumentFormattingEdits(
|
|
439
|
-
document: vscode.TextDocument,
|
|
440
|
-
options: vscode.FormattingOptions,
|
|
441
|
-
token: vscode.CancellationToken
|
|
442
|
-
): vscode.TextEdit[] {
|
|
443
|
-
const text = document.getText();
|
|
444
|
-
|
|
445
|
-
// Parse the document
|
|
446
|
-
const parseResult = this.parser.parse(text);
|
|
447
|
-
|
|
448
|
-
if (parseResult.errors.length > 0) {
|
|
449
|
-
// Don't format if there are parse errors
|
|
450
|
-
return [];
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// Get user preferences from VS Code settings
|
|
454
|
-
const config = vscode.workspace.getConfiguration('turtle');
|
|
455
|
-
|
|
456
|
-
// Serialize with formatting options
|
|
457
|
-
const result = this.serializer.serialize(parseResult.quads, {
|
|
458
|
-
prefixes: parseResult.prefixes,
|
|
459
|
-
baseIri: parseResult.baseIri,
|
|
460
|
-
indent: options.insertSpaces ? ' '.repeat(options.tabSize) : '\t',
|
|
461
|
-
prettyPrint: true,
|
|
462
|
-
groupBySubject: true,
|
|
463
|
-
sort: config.get('sortStatements', true),
|
|
464
|
-
maxLineWidth: config.get('maxLineWidth', 80),
|
|
465
|
-
alignPredicates: config.get('alignPredicates', false),
|
|
466
|
-
blankNodeStyle: config.get('blankNodeStyle', 'auto'),
|
|
467
|
-
predicateListStyle: config.get('predicateListStyle', 'first-same-line'),
|
|
468
|
-
blankLinesBetweenSubjects: config.get('blankLinesBetweenSubjects', true)
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
// Return a single edit replacing the entire document
|
|
472
|
-
const fullRange = new vscode.Range(
|
|
473
|
-
document.positionAt(0),
|
|
474
|
-
document.positionAt(text.length)
|
|
475
|
-
);
|
|
476
|
-
|
|
477
|
-
return [vscode.TextEdit.replace(fullRange, result.output)];
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// Register the formatter in your extension's activate function
|
|
482
|
-
export function activate(context: vscode.ExtensionContext) {
|
|
483
|
-
context.subscriptions.push(
|
|
484
|
-
vscode.languages.registerDocumentFormattingEditProvider(
|
|
485
|
-
{ language: 'turtle', scheme: 'file' },
|
|
486
|
-
new TurtleDocumentFormatter()
|
|
487
|
-
)
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
### SPARQL Formatter for VS Code
|
|
493
|
-
|
|
494
|
-
```typescript
|
|
495
|
-
import * as vscode from 'vscode';
|
|
496
|
-
import { SparqlFormatter } from '@faubulous/mentor-rdf-serializers';
|
|
497
|
-
|
|
498
|
-
export class SparqlDocumentFormatter implements vscode.DocumentFormattingEditProvider {
|
|
499
|
-
private formatter = new SparqlFormatter();
|
|
500
|
-
|
|
501
|
-
provideDocumentFormattingEdits(
|
|
502
|
-
document: vscode.TextDocument,
|
|
503
|
-
options: vscode.FormattingOptions,
|
|
504
|
-
token: vscode.CancellationToken
|
|
505
|
-
): vscode.TextEdit[] {
|
|
506
|
-
const text = document.getText();
|
|
507
|
-
const config = vscode.workspace.getConfiguration('sparql');
|
|
508
|
-
|
|
509
|
-
const result = this.formatter.formatFromText(text, {
|
|
510
|
-
indent: options.insertSpaces ? ' '.repeat(options.tabSize) : '\t',
|
|
511
|
-
prettyPrint: true,
|
|
512
|
-
uppercaseKeywords: config.get('uppercaseKeywords', true),
|
|
513
|
-
alignPatterns: config.get('alignPatterns', true),
|
|
514
|
-
sameBraceLine: config.get('sameBraceLine', true),
|
|
515
|
-
separateClauses: config.get('separateClauses', true),
|
|
516
|
-
maxLineWidth: config.get('maxLineWidth', 80)
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
const fullRange = new vscode.Range(
|
|
520
|
-
document.positionAt(0),
|
|
521
|
-
document.positionAt(text.length)
|
|
522
|
-
);
|
|
523
|
-
|
|
524
|
-
return [vscode.TextEdit.replace(fullRange, result.output)];
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
### Range Formatting (Format Selection)
|
|
530
|
-
|
|
531
|
-
For formatting only a selected range, use the `TokenSerializer` to preserve surrounding content:
|
|
532
|
-
|
|
533
|
-
```typescript
|
|
534
|
-
import { TurtleParser } from '@faubulous/mentor-rdf-parsers';
|
|
535
|
-
import { TokenSerializer } from '@faubulous/mentor-rdf-serializers';
|
|
536
|
-
|
|
537
|
-
export class TurtleRangeFormatter implements vscode.DocumentRangeFormattingEditProvider {
|
|
538
|
-
private parser = new TurtleParser();
|
|
539
|
-
private tokenSerializer = new TokenSerializer();
|
|
540
|
-
|
|
541
|
-
provideDocumentRangeFormattingEdits(
|
|
542
|
-
document: vscode.TextDocument,
|
|
543
|
-
range: vscode.Range,
|
|
544
|
-
options: vscode.FormattingOptions,
|
|
545
|
-
token: vscode.CancellationToken
|
|
546
|
-
): vscode.TextEdit[] {
|
|
547
|
-
const text = document.getText();
|
|
548
|
-
const parseResult = this.parser.parse(text);
|
|
549
|
-
|
|
550
|
-
if (parseResult.errors.length > 0) {
|
|
551
|
-
return [];
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// Get byte offsets for the selected range
|
|
555
|
-
const startOffset = document.offsetAt(range.start);
|
|
556
|
-
const endOffset = document.offsetAt(range.end);
|
|
557
|
-
|
|
558
|
-
// Serialize only tokens within the range
|
|
559
|
-
const result = this.tokenSerializer.serializeRange(
|
|
560
|
-
parseResult.tokens,
|
|
561
|
-
startOffset,
|
|
562
|
-
endOffset,
|
|
563
|
-
{ preserveComments: true, preserveBlankNodeIds: true }
|
|
564
|
-
);
|
|
565
|
-
|
|
566
|
-
return [vscode.TextEdit.replace(range, result.output)];
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
## License
|
|
88
|
+
## Related Projects
|
|
572
89
|
|
|
573
|
-
|
|
90
|
+
| Package | Description |
|
|
91
|
+
|---------|-------------|
|
|
92
|
+
| [@faubulous/mentor-rdf-parsers](https://github.com/faubulous/mentor-rdf-parsers) | RDF parsing library — the parser counterpart |
|
|
93
|
+
| [@faubulous/mentor-vscode](https://github.com/faubulous/mentor-vscode) | RDF IDE and workbench for VS Code |
|
|
574
94
|
|
|
575
95
|
## Contributing
|
|
576
96
|
|
|
577
|
-
Contributions are welcome
|
|
97
|
+
Contributions are welcome. Please feel free to open an issue or submit a pull request on [GitHub](https://github.com/faubulous/mentor-rdf-serializers).
|
|
578
98
|
|
|
579
|
-
##
|
|
99
|
+
## License
|
|
580
100
|
|
|
581
|
-
|
|
582
|
-
- [@faubulous/mentor-vscode](https://github.com/faubulous/mentor-vscode) - RDF IDE and workbench
|
|
101
|
+
[LGPL-2.1-or-later](https://github.com/faubulous/mentor-rdf-serializers/blob/main/LICENSE)
|
package/dist/index.d.ts
CHANGED
|
@@ -17,4 +17,6 @@ export { TrigFormatter, type TrigFormatterOptions } from './trig/formatter.js';
|
|
|
17
17
|
export { N3Formatter, type N3FormatterOptions } from './n3/formatter.js';
|
|
18
18
|
export { TokenSerializer, type Token, getBlankNodeIdFromToken, isBlankNodeToken, getTokenPosition } from './token-serializer.js';
|
|
19
19
|
export { alphabeticalStrategy, createByTypeStrategy, createSemanticStrategy, createPriorityStrategy, applySortingStrategy } from './sorting/index.js';
|
|
20
|
+
export type { StatementGroup, StatementGroupSerializerOptions, QuadInfo } from './statement-group.js';
|
|
21
|
+
export { groupCommentsWithQuads, mergeStatementGroups, sortStatementGroups, serializeStatementGroups } from './statement-group.js';
|
|
20
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACR,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,UAAU,EACV,cAAc,EACd,eAAe,EACf,kBAAkB,EACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EACH,SAAS,EACT,YAAY,EACZ,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,cAAc,EACd,UAAU,EACV,eAAe,EACf,SAAS,EACT,SAAS,EACT,QAAQ,EACR,mBAAmB,EACnB,4BAA4B,EAC5B,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,UAAU,EACV,WAAW,EACX,eAAe,EAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EACH,kBAAkB,EAClB,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,KAAK,EACV,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GAC5B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EACH,YAAY,EACZ,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,WAAW,EACX,iBAAiB,EACpB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACH,gBAAgB,EAChB,KAAK,uBAAuB,EAC/B,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACH,eAAe,EACf,KAAK,sBAAsB,EAC9B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACH,eAAe,EACf,KAAK,sBAAsB,EAC9B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,EACH,aAAa,EACb,KAAK,oBAAoB,EAC5B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACH,WAAW,EACX,KAAK,kBAAkB,EAC1B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACH,eAAe,EACf,KAAK,KAAK,EACV,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACH,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACvB,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACR,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,UAAU,EACV,cAAc,EACd,eAAe,EACf,kBAAkB,EACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EACH,SAAS,EACT,YAAY,EACZ,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,cAAc,EACd,UAAU,EACV,eAAe,EACf,SAAS,EACT,SAAS,EACT,QAAQ,EACR,mBAAmB,EACnB,4BAA4B,EAC5B,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,UAAU,EACV,WAAW,EACX,eAAe,EAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EACH,kBAAkB,EAClB,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,KAAK,EACV,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GAC5B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAG9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EACH,YAAY,EACZ,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,WAAW,EACX,iBAAiB,EACpB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACH,gBAAgB,EAChB,KAAK,uBAAuB,EAC/B,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACH,eAAe,EACf,KAAK,sBAAsB,EAC9B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACH,eAAe,EACf,KAAK,sBAAsB,EAC9B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,EACH,aAAa,EACb,KAAK,oBAAoB,EAC5B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACH,WAAW,EACX,KAAK,kBAAkB,EAC1B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACH,eAAe,EACf,KAAK,KAAK,EACV,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACH,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACvB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACR,cAAc,EACd,+BAA+B,EAC/B,QAAQ,EACX,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACH,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EAC3B,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -33,4 +33,5 @@ export { N3Formatter } from './n3/formatter.js';
|
|
|
33
33
|
export { TokenSerializer, getBlankNodeIdFromToken, isBlankNodeToken, getTokenPosition } from './token-serializer.js';
|
|
34
34
|
// Sorting strategies
|
|
35
35
|
export { alphabeticalStrategy, createByTypeStrategy, createSemanticStrategy, createPriorityStrategy, applySortingStrategy } from './sorting/index.js';
|
|
36
|
+
export { groupCommentsWithQuads, mergeStatementGroups, sortStatementGroups, serializeStatementGroups } from './statement-group.js';
|
|
36
37
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,YAAY;AACZ,OAAO,EACH,SAAS,EACT,YAAY,EACZ,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,cAAc,EACd,UAAU,EACV,eAAe,EACf,SAAS,EACT,SAAS,EACT,QAAQ,EACR,mBAAmB,EACnB,4BAA4B,EAC5B,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,UAAU,EACV,WAAW,EACX,eAAe,EAClB,MAAM,YAAY,CAAC;AAEpB,kBAAkB;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,uBAAuB;AACvB,OAAO,EACH,kBAAkB,EAClB,cAAc,GAMjB,MAAM,2BAA2B,CAAC;AAEnC,uBAAuB;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,qBAAqB;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,oBAAoB;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,kBAAkB;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,gBAAgB;AAChB,OAAO,EACH,YAAY,EAIZ,WAAW,EACX,iBAAiB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,qBAAqB;AACrB,OAAO,EACH,gBAAgB,EAEnB,MAAM,wBAAwB,CAAC;AAEhC,mBAAmB;AACnB,OAAO,EACH,eAAe,EAElB,MAAM,uBAAuB,CAAC;AAE/B,mBAAmB;AACnB,OAAO,EACH,eAAe,EAElB,MAAM,uBAAuB,CAAC;AAE/B,sBAAsB;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,oBAAoB;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,iBAAiB;AACjB,OAAO,EACH,aAAa,EAEhB,MAAM,qBAAqB,CAAC;AAE7B,eAAe;AACf,OAAO,EACH,WAAW,EAEd,MAAM,mBAAmB,CAAC;AAE3B,4BAA4B;AAC5B,OAAO,EACH,eAAe,EAEf,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,uBAAuB,CAAC;AAE/B,qBAAqB;AACrB,OAAO,EACH,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACvB,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,YAAY;AACZ,OAAO,EACH,SAAS,EACT,YAAY,EACZ,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,cAAc,EACd,UAAU,EACV,eAAe,EACf,SAAS,EACT,SAAS,EACT,QAAQ,EACR,mBAAmB,EACnB,4BAA4B,EAC5B,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,QAAQ,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,UAAU,EACV,WAAW,EACX,eAAe,EAClB,MAAM,YAAY,CAAC;AAEpB,kBAAkB;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,uBAAuB;AACvB,OAAO,EACH,kBAAkB,EAClB,cAAc,GAMjB,MAAM,2BAA2B,CAAC;AAEnC,uBAAuB;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,qBAAqB;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,oBAAoB;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,kBAAkB;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,gBAAgB;AAChB,OAAO,EACH,YAAY,EAIZ,WAAW,EACX,iBAAiB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,qBAAqB;AACrB,OAAO,EACH,gBAAgB,EAEnB,MAAM,wBAAwB,CAAC;AAEhC,mBAAmB;AACnB,OAAO,EACH,eAAe,EAElB,MAAM,uBAAuB,CAAC;AAE/B,mBAAmB;AACnB,OAAO,EACH,eAAe,EAElB,MAAM,uBAAuB,CAAC;AAE/B,sBAAsB;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,oBAAoB;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,iBAAiB;AACjB,OAAO,EACH,aAAa,EAEhB,MAAM,qBAAqB,CAAC;AAE7B,eAAe;AACf,OAAO,EACH,WAAW,EAEd,MAAM,mBAAmB,CAAC;AAE3B,4BAA4B;AAC5B,OAAO,EACH,eAAe,EAEf,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,uBAAuB,CAAC;AAE/B,qBAAqB;AACrB,OAAO,EACH,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACvB,MAAM,oBAAoB,CAAC;AAS5B,OAAO,EACH,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EAC3B,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comment-preserving statement groups for quad-based serialization.
|
|
3
|
+
*
|
|
4
|
+
* When a Turtle document is parsed into quads, source comments are lost because
|
|
5
|
+
* the RDF data model has no concept of comments. This module bridges that gap:
|
|
6
|
+
* it uses the parser's `QuadInfo` API (which pairs each quad with its source
|
|
7
|
+
* tokens) to associate nearby comment tokens with the quads they annotate.
|
|
8
|
+
*
|
|
9
|
+
* A `StatementGroup` bundles a quad with its leading and trailing comments so
|
|
10
|
+
* that sorting, merging, and re-serialization can move comments together with
|
|
11
|
+
* the statement they belong to.
|
|
12
|
+
*/
|
|
13
|
+
import type { IToken } from 'chevrotain';
|
|
14
|
+
import type { Quad } from '@rdfjs/types';
|
|
15
|
+
import type { Rdf12Quad, SortOption } from './types.js';
|
|
16
|
+
import type { QuadInfo } from '@faubulous/mentor-rdf-parsers';
|
|
17
|
+
export type { QuadInfo };
|
|
18
|
+
/**
|
|
19
|
+
* A quad together with the source comments that should stay attached to it.
|
|
20
|
+
*/
|
|
21
|
+
export interface StatementGroup {
|
|
22
|
+
/**
|
|
23
|
+
* The RDF quad (constructed from `QuadInfo.subject/predicate/object`).
|
|
24
|
+
* For groups created from `QuadInfo`, this is the materialized quad.
|
|
25
|
+
* For externally added quads, this is the quad as-is.
|
|
26
|
+
*/
|
|
27
|
+
quad: Quad | Rdf12Quad;
|
|
28
|
+
/**
|
|
29
|
+
* Comment lines that appeared immediately before the statement's subject
|
|
30
|
+
* in the source document (i.e. between the previous statement's end and
|
|
31
|
+
* this statement's subject token).
|
|
32
|
+
*/
|
|
33
|
+
leadingComments: IToken[];
|
|
34
|
+
/**
|
|
35
|
+
* A comment that appeared on the same line as the statement's terminating
|
|
36
|
+
* punctuation (`.` or `;` at the end of the statement).
|
|
37
|
+
*/
|
|
38
|
+
trailingComment?: IToken;
|
|
39
|
+
/**
|
|
40
|
+
* The source offset of the statement's first meaningful token (the subject).
|
|
41
|
+
* Used for sorting stability. `Infinity` for externally added quads.
|
|
42
|
+
*/
|
|
43
|
+
sourceOffset: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Builds `StatementGroup[]` from `QuadInfo[]` and the raw token stream.
|
|
47
|
+
*
|
|
48
|
+
* The algorithm:
|
|
49
|
+
* 1. Extract comment tokens from the token stream.
|
|
50
|
+
* 2. Derive statement spans from `QuadInfo` entries (subject offset → end of
|
|
51
|
+
* last component).
|
|
52
|
+
* 3. For each statement, claim all comment tokens whose `startOffset` lies
|
|
53
|
+
* between the end of the previous statement and the subject's
|
|
54
|
+
* `startOffset` as **leading comments**.
|
|
55
|
+
* 4. Any comment on the **same source line** as the statement's last token is
|
|
56
|
+
* claimed as the **trailing comment**.
|
|
57
|
+
* 5. Unclaimed comments at the very end of the document are attached to the
|
|
58
|
+
* last group (trailing) or returned as document-level trailing comments.
|
|
59
|
+
*
|
|
60
|
+
* @param quadInfos The `QuadInfo[]` returned by `TurtleReader.turtleDocInfo()`.
|
|
61
|
+
* @param tokens The full token array from `TurtleLexer.tokenize().tokens`
|
|
62
|
+
* (must include `COMMENT` tokens).
|
|
63
|
+
* @returns An array of statement groups with comments attached.
|
|
64
|
+
*/
|
|
65
|
+
export declare function groupCommentsWithQuads(quadInfos: QuadInfo[], tokens: IToken[]): StatementGroup[];
|
|
66
|
+
/**
|
|
67
|
+
* Merges externally added quads into an existing set of statement groups.
|
|
68
|
+
*
|
|
69
|
+
* New quads receive empty comment arrays so they serialize cleanly between
|
|
70
|
+
* existing groups.
|
|
71
|
+
*
|
|
72
|
+
* @param groups Existing statement groups (with comments attached).
|
|
73
|
+
* @param newQuads Additional quads to merge in.
|
|
74
|
+
* @param sort Sorting option — same values as `SerializerOptions.sort`.
|
|
75
|
+
* @returns A new array with all groups, sorted if requested.
|
|
76
|
+
*/
|
|
77
|
+
export declare function mergeStatementGroups(groups: StatementGroup[], newQuads: Array<Quad | Rdf12Quad>, sort?: SortOption): StatementGroup[];
|
|
78
|
+
/**
|
|
79
|
+
* Sorts statement groups using the provided sorting option.
|
|
80
|
+
*
|
|
81
|
+
* The sort operates on the *quad* inside each group — comments travel with
|
|
82
|
+
* their quad. When two quads compare equal the original source order is
|
|
83
|
+
* preserved (stable sort).
|
|
84
|
+
*/
|
|
85
|
+
export declare function sortStatementGroups(groups: StatementGroup[], sort: SortOption): StatementGroup[];
|
|
86
|
+
/**
|
|
87
|
+
* Options for `serializeStatementGroups`.
|
|
88
|
+
*/
|
|
89
|
+
export interface StatementGroupSerializerOptions {
|
|
90
|
+
/**
|
|
91
|
+
* Prefix mappings for compact IRI serialization.
|
|
92
|
+
*/
|
|
93
|
+
prefixes?: Record<string, string>;
|
|
94
|
+
/**
|
|
95
|
+
* Base IRI for relative references.
|
|
96
|
+
*/
|
|
97
|
+
baseIri?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Indentation string (default: `' '`).
|
|
100
|
+
*/
|
|
101
|
+
indent?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Line ending string (default: `'\n'`).
|
|
104
|
+
*/
|
|
105
|
+
lineEnd?: string;
|
|
106
|
+
/**
|
|
107
|
+
* Whether to insert blank lines between subject blocks (default: `true`).
|
|
108
|
+
*/
|
|
109
|
+
blankLinesBetweenSubjects?: boolean;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Serializes statement groups to Turtle, preserving comments.
|
|
113
|
+
*
|
|
114
|
+
* This is a lightweight serialization path that emits each group's leading
|
|
115
|
+
* comments, then the serialized quad, then the trailing comment. It delegates
|
|
116
|
+
* the actual quad serialization to a provided callback so it can be used with
|
|
117
|
+
* any serializer.
|
|
118
|
+
*
|
|
119
|
+
* @param groups The (optionally sorted/merged) statement groups.
|
|
120
|
+
* @param serializeQuad A function that serializes a single quad to a string
|
|
121
|
+
* (e.g. `TurtleSerializer.serializeQuad`).
|
|
122
|
+
* @param options Formatting options.
|
|
123
|
+
* @returns The serialized Turtle string with comments preserved.
|
|
124
|
+
*/
|
|
125
|
+
export declare function serializeStatementGroups(groups: StatementGroup[], serializeQuad: (quad: Quad | Rdf12Quad) => string, options?: StatementGroupSerializerOptions): string;
|
|
126
|
+
//# sourceMappingURL=statement-group.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statement-group.d.ts","sourceRoot":"","sources":["../src/statement-group.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAMxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,YAAY,EAAE,QAAQ,EAAE,CAAC;AAMzB;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B;;;;OAIG;IACH,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;IAEvB;;;;OAIG;IACH,eAAe,EAAE,MAAM,EAAE,CAAC;IAE1B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;CACxB;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,CAClC,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,MAAM,EAAE,GACjB,cAAc,EAAE,CAyFlB;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,cAAc,EAAE,EACxB,QAAQ,EAAE,KAAK,CAAC,IAAI,GAAG,SAAS,CAAC,EACjC,IAAI,CAAC,EAAE,UAAU,GAClB,cAAc,EAAE,CAmBlB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAC/B,MAAM,EAAE,cAAc,EAAE,EACxB,IAAI,EAAE,UAAU,GACjB,cAAc,EAAE,CAYlB;AAMD;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC5C;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAElC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACvC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACpC,MAAM,EAAE,cAAc,EAAE,EACxB,aAAa,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,KAAK,MAAM,EACjD,OAAO,CAAC,EAAE,+BAA+B,GAC1C,MAAM,CAiDR"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comment-preserving statement groups for quad-based serialization.
|
|
3
|
+
*
|
|
4
|
+
* When a Turtle document is parsed into quads, source comments are lost because
|
|
5
|
+
* the RDF data model has no concept of comments. This module bridges that gap:
|
|
6
|
+
* it uses the parser's `QuadInfo` API (which pairs each quad with its source
|
|
7
|
+
* tokens) to associate nearby comment tokens with the quads they annotate.
|
|
8
|
+
*
|
|
9
|
+
* A `StatementGroup` bundles a quad with its leading and trailing comments so
|
|
10
|
+
* that sorting, merging, and re-serialization can move comments together with
|
|
11
|
+
* the statement they belong to.
|
|
12
|
+
*/
|
|
13
|
+
import DataFactory from '@rdfjs/data-model';
|
|
14
|
+
import { applySortingStrategy } from './sorting/strategies.js';
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Grouping
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/**
|
|
19
|
+
* Builds `StatementGroup[]` from `QuadInfo[]` and the raw token stream.
|
|
20
|
+
*
|
|
21
|
+
* The algorithm:
|
|
22
|
+
* 1. Extract comment tokens from the token stream.
|
|
23
|
+
* 2. Derive statement spans from `QuadInfo` entries (subject offset → end of
|
|
24
|
+
* last component).
|
|
25
|
+
* 3. For each statement, claim all comment tokens whose `startOffset` lies
|
|
26
|
+
* between the end of the previous statement and the subject's
|
|
27
|
+
* `startOffset` as **leading comments**.
|
|
28
|
+
* 4. Any comment on the **same source line** as the statement's last token is
|
|
29
|
+
* claimed as the **trailing comment**.
|
|
30
|
+
* 5. Unclaimed comments at the very end of the document are attached to the
|
|
31
|
+
* last group (trailing) or returned as document-level trailing comments.
|
|
32
|
+
*
|
|
33
|
+
* @param quadInfos The `QuadInfo[]` returned by `TurtleReader.turtleDocInfo()`.
|
|
34
|
+
* @param tokens The full token array from `TurtleLexer.tokenize().tokens`
|
|
35
|
+
* (must include `COMMENT` tokens).
|
|
36
|
+
* @returns An array of statement groups with comments attached.
|
|
37
|
+
*/
|
|
38
|
+
export function groupCommentsWithQuads(quadInfos, tokens) {
|
|
39
|
+
if (quadInfos.length === 0) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
// 1. Collect all comment tokens, sorted by offset.
|
|
43
|
+
const comments = tokens
|
|
44
|
+
.filter(t => t.tokenType.name === 'COMMENT')
|
|
45
|
+
.sort((a, b) => a.startOffset - b.startOffset);
|
|
46
|
+
if (comments.length === 0) {
|
|
47
|
+
// No comments → simple groups.
|
|
48
|
+
return quadInfos.map(qi => ({
|
|
49
|
+
quad: materializeQuad(qi),
|
|
50
|
+
leadingComments: [],
|
|
51
|
+
trailingComment: undefined,
|
|
52
|
+
sourceOffset: qi.subject.token.startOffset,
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
// 2. Build statement info sorted by subject offset.
|
|
56
|
+
// Multiple QuadInfos may share the same subject (grouped statements),
|
|
57
|
+
// so we group by subject key + startOffset to find per-subject spans.
|
|
58
|
+
const subjectGroups = groupBySubject(quadInfos);
|
|
59
|
+
// 3. Walk through subject groups and claim comments.
|
|
60
|
+
const result = [];
|
|
61
|
+
let commentIdx = 0;
|
|
62
|
+
let previousStatementEnd = -1; // end offset of the previous statement span
|
|
63
|
+
for (const sg of subjectGroups) {
|
|
64
|
+
const subjectOffset = sg.subjectStartOffset;
|
|
65
|
+
const statementEndOffset = sg.endOffset;
|
|
66
|
+
const statementEndLine = sg.endLine;
|
|
67
|
+
// Collect leading comments: those between previousStatementEnd and subjectOffset.
|
|
68
|
+
const leading = [];
|
|
69
|
+
while (commentIdx < comments.length) {
|
|
70
|
+
const c = comments[commentIdx];
|
|
71
|
+
if (c.startOffset < subjectOffset && c.startOffset > previousStatementEnd) {
|
|
72
|
+
leading.push(c);
|
|
73
|
+
commentIdx++;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Collect trailing comment: a comment on the same line as the statement end.
|
|
80
|
+
let trailing;
|
|
81
|
+
if (commentIdx < comments.length &&
|
|
82
|
+
comments[commentIdx].startOffset > statementEndOffset &&
|
|
83
|
+
comments[commentIdx].startLine === statementEndLine) {
|
|
84
|
+
trailing = comments[commentIdx];
|
|
85
|
+
commentIdx++;
|
|
86
|
+
}
|
|
87
|
+
// Create one StatementGroup per quad in this subject group.
|
|
88
|
+
// Only the first quad gets the leading comments; only the last gets the trailing.
|
|
89
|
+
for (let i = 0; i < sg.quadInfos.length; i++) {
|
|
90
|
+
result.push({
|
|
91
|
+
quad: materializeQuad(sg.quadInfos[i]),
|
|
92
|
+
leadingComments: i === 0 ? leading : [],
|
|
93
|
+
trailingComment: i === sg.quadInfos.length - 1 ? trailing : undefined,
|
|
94
|
+
sourceOffset: sg.quadInfos[i].subject.token.startOffset,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
previousStatementEnd = statementEndOffset;
|
|
98
|
+
}
|
|
99
|
+
// 4. Remaining trailing comments → attach to the last group.
|
|
100
|
+
if (commentIdx < comments.length && result.length > 0) {
|
|
101
|
+
const last = result[result.length - 1];
|
|
102
|
+
const lastEndLine = subjectGroups[subjectGroups.length - 1].endLine;
|
|
103
|
+
while (commentIdx < comments.length) {
|
|
104
|
+
if (!last.trailingComment && comments[commentIdx].startLine === lastEndLine) {
|
|
105
|
+
last.trailingComment = comments[commentIdx];
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// Append as an additional leading comment on the last group
|
|
109
|
+
// (they'll appear after the statement — this is a "document footer" comment).
|
|
110
|
+
last.leadingComments.push(comments[commentIdx]);
|
|
111
|
+
}
|
|
112
|
+
commentIdx++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Merging & sorting
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
/**
|
|
121
|
+
* Merges externally added quads into an existing set of statement groups.
|
|
122
|
+
*
|
|
123
|
+
* New quads receive empty comment arrays so they serialize cleanly between
|
|
124
|
+
* existing groups.
|
|
125
|
+
*
|
|
126
|
+
* @param groups Existing statement groups (with comments attached).
|
|
127
|
+
* @param newQuads Additional quads to merge in.
|
|
128
|
+
* @param sort Sorting option — same values as `SerializerOptions.sort`.
|
|
129
|
+
* @returns A new array with all groups, sorted if requested.
|
|
130
|
+
*/
|
|
131
|
+
export function mergeStatementGroups(groups, newQuads, sort) {
|
|
132
|
+
const newGroups = newQuads.map(q => ({
|
|
133
|
+
quad: q,
|
|
134
|
+
leadingComments: [],
|
|
135
|
+
trailingComment: undefined,
|
|
136
|
+
sourceOffset: Infinity, // new quads sort after existing ones at the same rank
|
|
137
|
+
}));
|
|
138
|
+
const all = [...groups, ...newGroups];
|
|
139
|
+
if (sort === false) {
|
|
140
|
+
return all;
|
|
141
|
+
}
|
|
142
|
+
if (!sort) {
|
|
143
|
+
return all;
|
|
144
|
+
}
|
|
145
|
+
return sortStatementGroups(all, sort);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Sorts statement groups using the provided sorting option.
|
|
149
|
+
*
|
|
150
|
+
* The sort operates on the *quad* inside each group — comments travel with
|
|
151
|
+
* their quad. When two quads compare equal the original source order is
|
|
152
|
+
* preserved (stable sort).
|
|
153
|
+
*/
|
|
154
|
+
export function sortStatementGroups(groups, sort) {
|
|
155
|
+
// Extract quads, sort them, then map the sorted order back to groups.
|
|
156
|
+
const quads = groups.map(g => g.quad);
|
|
157
|
+
const sorted = applySortingStrategy(quads, sort);
|
|
158
|
+
// Build a lookup: quad identity → group (using reference equality).
|
|
159
|
+
const quadToGroup = new Map();
|
|
160
|
+
for (const g of groups) {
|
|
161
|
+
quadToGroup.set(g.quad, g);
|
|
162
|
+
}
|
|
163
|
+
return sorted.map(q => quadToGroup.get(q));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Serializes statement groups to Turtle, preserving comments.
|
|
167
|
+
*
|
|
168
|
+
* This is a lightweight serialization path that emits each group's leading
|
|
169
|
+
* comments, then the serialized quad, then the trailing comment. It delegates
|
|
170
|
+
* the actual quad serialization to a provided callback so it can be used with
|
|
171
|
+
* any serializer.
|
|
172
|
+
*
|
|
173
|
+
* @param groups The (optionally sorted/merged) statement groups.
|
|
174
|
+
* @param serializeQuad A function that serializes a single quad to a string
|
|
175
|
+
* (e.g. `TurtleSerializer.serializeQuad`).
|
|
176
|
+
* @param options Formatting options.
|
|
177
|
+
* @returns The serialized Turtle string with comments preserved.
|
|
178
|
+
*/
|
|
179
|
+
export function serializeStatementGroups(groups, serializeQuad, options) {
|
|
180
|
+
const le = options?.lineEnd ?? '\n';
|
|
181
|
+
const blankLines = options?.blankLinesBetweenSubjects ?? true;
|
|
182
|
+
const parts = [];
|
|
183
|
+
// Emit prefix declarations.
|
|
184
|
+
if (options?.baseIri) {
|
|
185
|
+
parts.push(`@base <${options.baseIri}> .`);
|
|
186
|
+
}
|
|
187
|
+
if (options?.prefixes) {
|
|
188
|
+
for (const [prefix, namespace] of Object.entries(options.prefixes)) {
|
|
189
|
+
parts.push(`@prefix ${prefix}: <${namespace}> .`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (parts.length > 0) {
|
|
193
|
+
parts.push(''); // blank line after declarations
|
|
194
|
+
}
|
|
195
|
+
let lastSubjectValue = null;
|
|
196
|
+
for (const group of groups) {
|
|
197
|
+
const subject = group.quad.subject;
|
|
198
|
+
const subjectValue = 'value' in subject ? subject.value : String(subject);
|
|
199
|
+
const isNewSubject = subjectValue !== lastSubjectValue;
|
|
200
|
+
// Blank line between subject blocks.
|
|
201
|
+
if (isNewSubject && lastSubjectValue !== null && blankLines) {
|
|
202
|
+
parts.push('');
|
|
203
|
+
}
|
|
204
|
+
// Leading comments.
|
|
205
|
+
for (const comment of group.leadingComments) {
|
|
206
|
+
parts.push(comment.image);
|
|
207
|
+
}
|
|
208
|
+
// The quad itself.
|
|
209
|
+
let line = serializeQuad(group.quad);
|
|
210
|
+
// Trailing comment on the same line.
|
|
211
|
+
if (group.trailingComment) {
|
|
212
|
+
line += ' ' + group.trailingComment.image;
|
|
213
|
+
}
|
|
214
|
+
parts.push(line);
|
|
215
|
+
lastSubjectValue = subjectValue;
|
|
216
|
+
}
|
|
217
|
+
return parts.join(le);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Groups consecutive `QuadInfo` entries that share the same subject into spans,
|
|
221
|
+
* sorted by the subject's source offset. This mirrors Turtle's subject
|
|
222
|
+
* grouping: `ex:S ex:p1 "a" ; ex:p2 "b" .` produces two QuadInfos with the
|
|
223
|
+
* same subject offset but different predicate/object offsets.
|
|
224
|
+
*/
|
|
225
|
+
function groupBySubject(quadInfos) {
|
|
226
|
+
const spans = [];
|
|
227
|
+
let current = null;
|
|
228
|
+
for (const qi of quadInfos) {
|
|
229
|
+
const subjKey = qi.subject.term.value;
|
|
230
|
+
const subjOffset = qi.subject.token.startOffset;
|
|
231
|
+
if (!current || current.subjectKey !== subjKey || current.subjectStartOffset !== subjOffset) {
|
|
232
|
+
current = {
|
|
233
|
+
subjectKey: subjKey,
|
|
234
|
+
subjectStartOffset: subjOffset,
|
|
235
|
+
endOffset: maxEndOffset(qi),
|
|
236
|
+
endLine: maxEndLine(qi),
|
|
237
|
+
quadInfos: [qi],
|
|
238
|
+
};
|
|
239
|
+
spans.push(current);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
current.quadInfos.push(qi);
|
|
243
|
+
const end = maxEndOffset(qi);
|
|
244
|
+
const line = maxEndLine(qi);
|
|
245
|
+
if (end > current.endOffset)
|
|
246
|
+
current.endOffset = end;
|
|
247
|
+
if (line > current.endLine)
|
|
248
|
+
current.endLine = line;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return spans;
|
|
252
|
+
}
|
|
253
|
+
function maxEndOffset(qi) {
|
|
254
|
+
let end = qi.object.token.endOffset ?? qi.object.token.startOffset;
|
|
255
|
+
if (qi.predicate.token.endOffset !== undefined && qi.predicate.token.endOffset > end) {
|
|
256
|
+
end = qi.predicate.token.endOffset;
|
|
257
|
+
}
|
|
258
|
+
return end;
|
|
259
|
+
}
|
|
260
|
+
function maxEndLine(qi) {
|
|
261
|
+
return qi.object.token.endLine ?? qi.object.token.startLine ?? 0;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Materializes a `QuadInfo` into an RDF/JS-compatible `Quad` object.
|
|
265
|
+
* We build a minimal quad from the term references in the QuadInfo.
|
|
266
|
+
*/
|
|
267
|
+
function materializeQuad(qi) {
|
|
268
|
+
const graph = qi.graph?.term ?? DataFactory.defaultGraph();
|
|
269
|
+
return DataFactory.quad(qi.subject.term, qi.predicate.term, qi.object.term, graph);
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=statement-group.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statement-group.js","sourceRoot":"","sources":["../src/statement-group.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,WAAW,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AA2C/D,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,sBAAsB,CAClC,SAAqB,EACrB,MAAgB;IAEhB,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC;IACd,CAAC;IAED,mDAAmD;IACnD,MAAM,QAAQ,GAAG,MAAM;SAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC;SAC3C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC;IAEnD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,+BAA+B;QAC/B,OAAO,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACxB,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC;YACzB,eAAe,EAAE,EAAE;YACnB,eAAe,EAAE,SAAS;YAC1B,YAAY,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW;SAC7C,CAAC,CAAC,CAAC;IACR,CAAC;IAED,oDAAoD;IACpD,yEAAyE;IACzE,yEAAyE;IACzE,MAAM,aAAa,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAEhD,qDAAqD;IACrD,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,oBAAoB,GAAG,CAAC,CAAC,CAAC,CAAC,4CAA4C;IAE3E,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;QAC7B,MAAM,aAAa,GAAG,EAAE,CAAC,kBAAkB,CAAC;QAC5C,MAAM,kBAAkB,GAAG,EAAE,CAAC,SAAS,CAAC;QACxC,MAAM,gBAAgB,GAAG,EAAE,CAAC,OAAO,CAAC;QAEpC,kFAAkF;QAClF,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,OAAO,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;YAClC,MAAM,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,WAAW,GAAG,aAAa,IAAI,CAAC,CAAC,WAAW,GAAG,oBAAoB,EAAE,CAAC;gBACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAChB,UAAU,EAAE,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACJ,MAAM;YACV,CAAC;QACL,CAAC;QAED,6EAA6E;QAC7E,IAAI,QAA4B,CAAC;QACjC,IACI,UAAU,GAAG,QAAQ,CAAC,MAAM;YAC5B,QAAQ,CAAC,UAAU,CAAC,CAAC,WAAW,GAAG,kBAAkB;YACrD,QAAQ,CAAC,UAAU,CAAC,CAAC,SAAS,KAAK,gBAAgB,EACrD,CAAC;YACC,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;YAChC,UAAU,EAAE,CAAC;QACjB,CAAC;QAED,4DAA4D;QAC5D,kFAAkF;QAClF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC;gBACR,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBACtC,eAAe,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;gBACvC,eAAe,EAAE,CAAC,KAAK,EAAE,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;gBACrE,YAAY,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW;aAC1D,CAAC,CAAC;QACP,CAAC;QAED,oBAAoB,GAAG,kBAAkB,CAAC;IAC9C,CAAC;IAED,6DAA6D;IAC7D,IAAI,UAAU,GAAG,QAAQ,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvC,MAAM,WAAW,GAAG,aAAa,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;QACpE,OAAO,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC,SAAS,KAAK,WAAW,EAAE,CAAC;gBAC1E,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;YAChD,CAAC;iBAAM,CAAC;gBACJ,4DAA4D;gBAC5D,8EAA8E;gBAC9E,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;YACpD,CAAC;YACD,UAAU,EAAE,CAAC;QACjB,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAChC,MAAwB,EACxB,QAAiC,EACjC,IAAiB;IAEjB,MAAM,SAAS,GAAqB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACnD,IAAI,EAAE,CAAC;QACP,eAAe,EAAE,EAAE;QACnB,eAAe,EAAE,SAAS;QAC1B,YAAY,EAAE,QAAQ,EAAE,sDAAsD;KACjF,CAAC,CAAC,CAAC;IAEJ,MAAM,GAAG,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IAEtC,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACjB,OAAO,GAAG,CAAC;IACf,CAAC;IAED,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,OAAO,GAAG,CAAC;IACf,CAAC;IAED,OAAO,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAC/B,MAAwB,EACxB,IAAgB;IAEhB,sEAAsE;IACtE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,MAAM,GAA4B,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAE1E,oEAAoE;IACpE,MAAM,WAAW,GAAG,IAAI,GAAG,EAAoC,CAAC;IAChE,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACrB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC;AAChD,CAAC;AAoCD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,wBAAwB,CACpC,MAAwB,EACxB,aAAiD,EACjD,OAAyC;IAEzC,MAAM,EAAE,GAAG,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC;IACpC,MAAM,UAAU,GAAG,OAAO,EAAE,yBAAyB,IAAI,IAAI,CAAC;IAE9D,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,4BAA4B;IAC5B,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,UAAU,OAAO,CAAC,OAAO,KAAK,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,OAAO,EAAE,QAAQ,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjE,KAAK,CAAC,IAAI,CAAC,WAAW,MAAM,MAAM,SAAS,KAAK,CAAC,CAAC;QACtD,CAAC;IACL,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,gCAAgC;IACpD,CAAC;IAED,IAAI,gBAAgB,GAAkB,IAAI,CAAC;IAE3C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;QACnC,MAAM,YAAY,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1E,MAAM,YAAY,GAAG,YAAY,KAAK,gBAAgB,CAAC;QAEvD,qCAAqC;QACrC,IAAI,YAAY,IAAI,gBAAgB,KAAK,IAAI,IAAI,UAAU,EAAE,CAAC;YAC1D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;QAED,oBAAoB;QACpB,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;YAC1C,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;QAED,mBAAmB;QACnB,IAAI,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAErC,qCAAqC;QACrC,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;YACxB,IAAI,IAAI,GAAG,GAAG,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC;QAC9C,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,gBAAgB,GAAG,YAAY,CAAC;IACpC,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC;AAeD;;;;;GAKG;AACH,SAAS,cAAc,CAAC,SAAqB;IACzC,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,IAAI,OAAO,GAAuB,IAAI,CAAC;IAEvC,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QACtC,MAAM,UAAU,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC;QAEhD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO,IAAI,OAAO,CAAC,kBAAkB,KAAK,UAAU,EAAE,CAAC;YAC1F,OAAO,GAAG;gBACN,UAAU,EAAE,OAAO;gBACnB,kBAAkB,EAAE,UAAU;gBAC9B,SAAS,EAAE,YAAY,CAAC,EAAE,CAAC;gBAC3B,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;gBACvB,SAAS,EAAE,CAAC,EAAE,CAAC;aAClB,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC3B,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;YAC7B,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;YAC5B,IAAI,GAAG,GAAG,OAAO,CAAC,SAAS;gBAAE,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC;YACrD,IAAI,IAAI,GAAG,OAAO,CAAC,OAAO;gBAAE,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvD,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,YAAY,CAAC,EAAY;IAC9B,IAAI,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC;IACnE,IAAI,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC;QACnF,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC;IACvC,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,SAAS,UAAU,CAAC,EAAY;IAC5B,OAAO,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC;AACrE,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,EAAY;IACjC,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,EAAE,IAAI,IAAI,WAAW,CAAC,YAAY,EAAE,CAAC;IAE3D,OAAO,WAAW,CAAC,IAAI,CACnB,EAAE,CAAC,OAAO,CAAC,IAAI,EACf,EAAE,CAAC,SAAS,CAAC,IAAI,EACjB,EAAE,CAAC,MAAM,CAAC,IAAI,EACd,KAAK,CACW,CAAC;AACzB,CAAC"}
|