Skip to content

Commit 07e5cd5

Browse files
gkelloggdavidlehn
authored andcommitted
Change @base handling to improve cachability.
Don't set `@base` in initial context and don't resolve a relative IRI when setting `@base` in a context, so that the document location can be kept separate from the context itself. This makes the initial context the same for all processingModes, allowing much more cachability of remote and resolved contexts.
1 parent 8c4c060 commit 07e5cd5

File tree

4 files changed

+50
-24
lines changed

4 files changed

+50
-24
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
### Changed
88
- Better support for using a processed context for `null` and caching
99
`@import`.
10+
- Don't set `@base` in initial context and don't resolve a relative IRI
11+
when setting `@base` in a context, so that the document location can
12+
be kept separate from the context itself.
1013

1114
## 3.0.1 - 2020-03-10
1215

lib/compact.js

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ const {
2929
} = require('./context');
3030

3131
const {
32-
removeBase: _removeBase
32+
removeBase: _removeBase,
33+
prependBase: _prependBase
3334
} = require('./url');
3435

3536
const {
@@ -226,7 +227,8 @@ api.compact = async ({
226227
expandedIri => api.compactIri({
227228
activeCtx,
228229
iri: expandedIri,
229-
relativeTo: {vocab: false}
230+
relativeTo: {vocab: false},
231+
base: options.base
230232
}));
231233
if(compactedValue.length === 1) {
232234
compactedValue = compactedValue[0];
@@ -485,7 +487,8 @@ api.compact = async ({
485487
// index on @id or @index or alias of @none
486488
const key = (container.includes('@id') ?
487489
expandedItem['@id'] : expandedItem['@index']) ||
488-
api.compactIri({activeCtx, iri: '@none', vocab: true});
490+
api.compactIri({activeCtx, iri: '@none',
491+
relativeTo: {vocab: true}});
489492
// add compactedItem to map, using value of `@id` or a new blank
490493
// node identifier
491494

@@ -570,7 +573,7 @@ api.compact = async ({
570573
const indexKey = _getContextValue(
571574
activeCtx, itemActiveProperty, '@index') || '@index';
572575
const containerKey = api.compactIri(
573-
{activeCtx, iri: indexKey, vocab: true});
576+
{activeCtx, iri: indexKey, relativeTo: {vocab: true}});
574577
if(indexKey === '@index') {
575578
key = expandedItem['@index'];
576579
delete compactedItem[containerKey];
@@ -595,14 +598,15 @@ api.compact = async ({
595598
}
596599
}
597600
} else if(container.includes('@id')) {
598-
const idKey = api.compactIri({activeCtx, iri: '@id', vocab: true});
601+
const idKey = api.compactIri({activeCtx, iri: '@id',
602+
relativeTo: {vocab: true}});
599603
key = compactedItem[idKey];
600604
delete compactedItem[idKey];
601605
} else if(container.includes('@type')) {
602606
const typeKey = api.compactIri({
603607
activeCtx,
604608
iri: '@type',
605-
vocab: true
609+
relativeTo: {vocab: true}
606610
});
607611
let types;
608612
[key, ...types] = _asArray(compactedItem[typeKey] || []);
@@ -634,7 +638,8 @@ api.compact = async ({
634638

635639
// if compacting this value which has no key, index on @none
636640
if(!key) {
637-
key = api.compactIri({activeCtx, iri: '@none', vocab: true});
641+
key = api.compactIri({activeCtx, iri: '@none',
642+
relativeTo: {vocab: true}});
638643
}
639644
// add compact value to map object using key from expanded value
640645
// based on the container type
@@ -676,6 +681,7 @@ api.compact = async ({
676681
* @param relativeTo options for how to compact IRIs:
677682
* vocab: true to split after @vocab, false not to.
678683
* @param reverse true if a reverse property is being compacted, false if not.
684+
* @param base the absolute URL to use for compacting document-relative IRIs.
679685
*
680686
* @return the compacted term, prefix, keyword alias, or the original IRI.
681687
*/
@@ -684,7 +690,8 @@ api.compactIri = ({
684690
iri,
685691
value = null,
686692
relativeTo = {vocab: false},
687-
reverse = false
693+
reverse = false,
694+
base = null
688695
}) => {
689696
// can't compact null
690697
if(iri === null) {
@@ -933,7 +940,16 @@ api.compactIri = ({
933940

934941
// compact IRI relative to base
935942
if(!relativeTo.vocab) {
936-
return _removeBase(activeCtx['@base'], iri);
943+
if('@base' in activeCtx) {
944+
if(!activeCtx['@base']) {
945+
// The None case preserves rval as potentially relative
946+
return iri;
947+
} else {
948+
return _removeBase(_prependBase(base, activeCtx['@base']), iri);
949+
}
950+
} else {
951+
return _removeBase(base, iri);
952+
}
937953
}
938954

939955
// return IRI as is
@@ -1050,8 +1066,11 @@ api.compactValue = ({activeCtx, activeProperty, value, options}) => {
10501066
const expandedProperty = _expandIri(activeCtx, activeProperty, {vocab: true},
10511067
options);
10521068
const type = _getContextValue(activeCtx, activeProperty, '@type');
1053-
const compacted = api.compactIri(
1054-
{activeCtx, iri: value['@id'], relativeTo: {vocab: type === '@vocab'}});
1069+
const compacted = api.compactIri({
1070+
activeCtx,
1071+
iri: value['@id'],
1072+
relativeTo: {vocab: type === '@vocab'},
1073+
base: options.base});
10551074

10561075
// compact to scalar
10571076
if(type === '@id' || type === '@vocab' || expandedProperty === '@graph') {

lib/context.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,10 @@ api.process = async ({
201201
if('@base' in ctx) {
202202
let base = ctx['@base'];
203203

204-
if(base === null) {
204+
if(base === null || _isAbsoluteIri(base)) {
205205
// no action
206-
} else if(_isAbsoluteIri(base)) {
207-
base = parseUrl(base);
208206
} else if(_isRelativeIri(base)) {
209-
base = parseUrl(prependBase(rval['@base'].href, base));
207+
base = prependBase(rval['@base'], base);
210208
} else {
211209
throw new JsonLdError(
212210
'Invalid JSON-LD syntax; the value of "@base" in a ' +
@@ -341,7 +339,8 @@ api.process = async ({
341339
const importCtx = resolvedImport[0].document;
342340
if('@import' in importCtx) {
343341
throw new JsonLdError(
344-
'Invalid JSON-LD syntax; imported context must not include @import.',
342+
'Invalid JSON-LD syntax: ' +
343+
'imported context must not include @import.',
345344
'jsonld.SyntaxError',
346345
{code: 'invalid context entry', context: localCtx});
347346
}
@@ -1048,8 +1047,13 @@ function _expandIri(activeCtx, value, relativeTo, localCtx, defined, options) {
10481047
}
10491048

10501049
// prepend base
1051-
if(relativeTo.base) {
1052-
return prependBase(activeCtx['@base'], value);
1050+
if(relativeTo.base && '@base' in activeCtx) {
1051+
if(activeCtx['@base']) {
1052+
// The null case preserves value as potentially relative
1053+
return prependBase(prependBase(options.base, activeCtx['@base']), value);
1054+
}
1055+
} else if(relativeTo.base) {
1056+
return prependBase(options.base, value);
10531057
}
10541058

10551059
return value;
@@ -1064,15 +1068,13 @@ function _expandIri(activeCtx, value, relativeTo, localCtx, defined, options) {
10641068
* @return the initial context.
10651069
*/
10661070
api.getInitialContext = options => {
1067-
const base = parseUrl(options.base || '');
1068-
const key = JSON.stringify({base, processingMode: options.processingMode});
1071+
const key = JSON.stringify({processingMode: options.processingMode});
10691072
const cached = INITIAL_CONTEXT_CACHE.get(key);
10701073
if(cached) {
10711074
return cached;
10721075
}
10731076

10741077
const initialContext = {
1075-
'@base': base,
10761078
processingMode: options.processingMode,
10771079
mappings: new Map(),
10781080
inverse: null,
@@ -1278,7 +1280,6 @@ api.getInitialContext = options => {
12781280
*/
12791281
function _cloneActiveContext() {
12801282
const child = {};
1281-
child['@base'] = this['@base'];
12821283
child.mappings = util.clone(this.mappings);
12831284
child.clone = this.clone;
12841285
child.inverse = null;
@@ -1288,6 +1289,9 @@ api.getInitialContext = options => {
12881289
child.previousContext = this.previousContext.clone();
12891290
}
12901291
child.revertToPreviousContext = this.revertToPreviousContext;
1292+
if('@base' in this) {
1293+
child['@base'] = this['@base'];
1294+
}
12911295
if('@language' in this) {
12921296
child['@language'] = this['@language'];
12931297
}

lib/url.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ api.prependBase = (base, iri) => {
7171
}
7272

7373
// parse base if it is a string
74-
if(types.isString(base)) {
74+
if(!base || types.isString(base)) {
7575
base = api.parse(base || '');
7676
}
7777

@@ -158,7 +158,7 @@ api.removeBase = (base, iri) => {
158158
return iri;
159159
}
160160

161-
if(types.isString(base)) {
161+
if(!base || types.isString(base)) {
162162
base = api.parse(base || '');
163163
}
164164

0 commit comments

Comments
 (0)