Skip to content

Commit 7935a8e

Browse files
committed
fix bundle producing schema with corrupted refs in some cases
1 parent 707e626 commit 7935a8e

File tree

5 files changed

+59
-10
lines changed

5 files changed

+59
-10
lines changed

lib/bundle.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function bundle (parser, options) {
2020

2121
// Build an inventory of all $ref pointers in the JSON Schema
2222
var inventory = [];
23-
crawl(parser, 'schema', parser.$refs._root$Ref.path + '#', '#', inventory, parser.$refs, options);
23+
crawl(parser, 'schema', parser.$refs._root$Ref.path + '#', '#', inventory, parser.$refs, 0, options);
2424

2525
// Remap all $ref pointers
2626
remap(inventory);
@@ -37,12 +37,12 @@ function bundle (parser, options) {
3737
* @param {$Refs} $refs
3838
* @param {$RefParserOptions} options
3939
*/
40-
function crawl (parent, key, path, pathFromRoot, inventory, $refs, options) {
40+
function crawl (parent, key, path, pathFromRoot, inventory, $refs, seq, options) {
4141
var obj = key === null ? parent : parent[key];
4242

4343
if (obj && typeof obj === 'object') {
4444
if ($Ref.isAllowed$Ref(obj)) {
45-
inventory$Ref(parent, key, path, pathFromRoot, inventory, $refs, options);
45+
inventory$Ref(parent, key, path, pathFromRoot, inventory, $refs, seq++, options);
4646
}
4747
else {
4848
var keys = Object.keys(obj);
@@ -60,10 +60,10 @@ function crawl (parent, key, path, pathFromRoot, inventory, $refs, options) {
6060
var value = obj[key];
6161

6262
if ($Ref.isAllowed$Ref(value)) {
63-
inventory$Ref(obj, key, path, keyPathFromRoot, inventory, $refs, options);
63+
inventory$Ref(obj, key, path, keyPathFromRoot, inventory, $refs, seq++, options);
6464
}
6565
else {
66-
crawl(obj, key, keyPath, keyPathFromRoot, inventory, $refs, options);
66+
crawl(obj, key, keyPath, keyPathFromRoot, inventory, $refs, seq, options);
6767
}
6868
});
6969
}
@@ -82,7 +82,7 @@ function crawl (parent, key, path, pathFromRoot, inventory, $refs, options) {
8282
* @param {$Refs} $refs
8383
* @param {$RefParserOptions} options
8484
*/
85-
function inventory$Ref ($refParent, $refKey, path, pathFromRoot, inventory, $refs, options) {
85+
function inventory$Ref ($refParent, $refKey, path, pathFromRoot, inventory, $refs, seq, options) {
8686
if (inventory.some(function (i) { return i.parent === $refParent && i.key === $refKey; })) {
8787
// This $Ref has already been inventoried, so we don't need to process it again
8888
return;
@@ -108,11 +108,14 @@ function inventory$Ref ($refParent, $refKey, path, pathFromRoot, inventory, $ref
108108
value: pointer.value, // The resolved value of the $ref pointer
109109
circular: pointer.circular, // Is this $ref pointer DIRECTLY circular? (i.e. it references itself)
110110
extended: extended, // Does this $ref extend its resolved value? (i.e. it has extra properties, in addition to "$ref")
111-
external: external // Does this $ref pointer point to a file other than the main JSON Schema file?
111+
external: external, // Does this $ref pointer point to a file other than the main JSON Schema file?
112+
seq: seq // Sequence number of this $ref
112113
});
113114

115+
var refPathFromRoot = external ? pathFromRoot : $ref.$ref;
116+
114117
// Recursively crawl the resolved value
115-
crawl(pointer.value, null, pointer.path, pathFromRoot, inventory, $refs, options);
118+
crawl(pointer.value, null, pointer.path, refPathFromRoot, inventory, $refs, seq, options);
116119
}
117120

118121
/**
@@ -157,8 +160,8 @@ function remap (inventory) {
157160
return a.depth - b.depth; // Sort $refs by how close they are to the JSON Schema root
158161
}
159162
else {
160-
// If all else is equal, use the $ref that's in the "definitions" property
161-
return b.pathFromRoot.lastIndexOf('/definitions') - a.pathFromRoot.lastIndexOf('/definitions');
163+
// If all else is equal, use the $ref that's in the "definitions" property or sequence number
164+
return b.pathFromRoot.lastIndexOf('/definitions') - a.pathFromRoot.lastIndexOf('/definitions') || a.seq - b.seq;
162165
}
163166
});
164167

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"thing": {
3+
"type": "string"
4+
}
5+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
helper.bundled.externalFromInternal = {
2+
paths: {
3+
$ref: '#/components'
4+
},
5+
external: {
6+
go: {
7+
deeper: {
8+
$ref: '#/components/test'
9+
}
10+
}
11+
},
12+
components: {
13+
test: {
14+
type: 'string'
15+
}
16+
}
17+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
describe('Schema with two external refs to the same value and internal ref before', function () {
2+
'use strict';
3+
/* details here: https://github.com/BigstickCarpet/json-schema-ref-parser/pull/62 */
4+
5+
it('should bundle successfully', function () {
6+
var parser = new $RefParser();
7+
return parser
8+
.bundle(path.rel('specs/external-from-internal/external-from-internal.yaml'))
9+
.then(function (schema) {
10+
expect(schema).to.equal(parser.schema);
11+
expect(schema).to.deep.equal(helper.bundled.externalFromInternal);
12+
});
13+
});
14+
});
15+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
paths:
2+
$ref: '#/components'
3+
external:
4+
go:
5+
deeper:
6+
$ref: 'definitions/definitions.json#/thing'
7+
components:
8+
test:
9+
$ref: 'definitions/definitions.json#/thing'

0 commit comments

Comments
 (0)