Skip to content

Commit 2b5620e

Browse files
committed
Export built-in types, type override now preserves order
1 parent ab31bba commit 2b5620e

File tree

9 files changed

+196
-11
lines changed

9 files changed

+196
-11
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
9+
## [4.1.0] - WIP
10+
### Added
11+
- Types are now exported as `yaml.types.XXX`.
12+
- Every type now has options object with original arguments kept as they were
13+
(see `yaml.types.int.options` as an example).
14+
15+
### Changed
16+
- Schema.extend now keeps old type order in case of conflicts
17+
(e.g. Schema.extend([ a, b, c ]).extend([ b, a, d ]) is now ordered as `abcd` instead of `cbad`).
18+
19+
820
## [4.0.0] - 2021-01-03
921
### Changed
1022
- Check [migration guide](migrate_v3_to_v4.md) to see details for all breaking changes.

examples/.eslintrc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ env:
22
es6: true
33

44
parserOptions:
5-
ecmaVersion: 2017
5+
ecmaVersion: 2020

examples/int_type_override.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// This example overrides built-in !!int type to return BigInt instead of a Number
2+
//
3+
4+
'use strict';
5+
6+
/*global BigInt*/
7+
/*eslint-disable no-console*/
8+
9+
const util = require('util');
10+
const yaml = require('../');
11+
12+
13+
// keep most of the original `int` options as is
14+
let options = Object.assign({}, yaml.types.int.options);
15+
16+
options.construct = data => {
17+
let value = data, sign = 1n, ch;
18+
19+
if (value.indexOf('_') !== -1) {
20+
value = value.replace(/_/g, '');
21+
}
22+
23+
ch = value[0];
24+
25+
if (ch === '-' || ch === '+') {
26+
if (ch === '-') sign = -1n;
27+
value = value.slice(1);
28+
ch = value[0];
29+
}
30+
31+
return sign * BigInt(value);
32+
};
33+
34+
35+
options.predicate = object => {
36+
return Object.prototype.toString.call(object) === '[object BigInt]' ||
37+
yaml.types.int.options.predicate(object);
38+
};
39+
40+
41+
let BigIntType = new yaml.Type('tag:yaml.org,2002:int', options);
42+
43+
const SCHEMA = yaml.DEFAULT_SCHEMA.extend({ implicit: [ BigIntType ] });
44+
45+
const data = `
46+
bigint: -12_345_678_901_234_567_890
47+
`;
48+
49+
const loaded = yaml.load(data, { schema: SCHEMA });
50+
51+
console.log('Parsed as:');
52+
console.log('-'.repeat(70));
53+
console.log(util.inspect(loaded, false, 20, true));
54+
55+
console.log('');
56+
console.log('');
57+
console.log('Dumped as:');
58+
console.log('-'.repeat(70));
59+
console.log(yaml.dump(loaded, { schema: SCHEMA }));

index.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@ module.exports.loadAll = loader.loadAll;
2424
module.exports.dump = dumper.dump;
2525
module.exports.YAMLException = require('./lib/exception');
2626

27+
// Re-export all types in case user wants to create custom schema
28+
module.exports.types = {
29+
binary: require('./lib/type/binary'),
30+
float: require('./lib/type/float'),
31+
map: require('./lib/type/map'),
32+
null: require('./lib/type/null'),
33+
pairs: require('./lib/type/pairs'),
34+
set: require('./lib/type/set'),
35+
timestamp: require('./lib/type/timestamp'),
36+
bool: require('./lib/type/bool'),
37+
int: require('./lib/type/int'),
38+
merge: require('./lib/type/merge'),
39+
omap: require('./lib/type/omap'),
40+
seq: require('./lib/type/seq'),
41+
str: require('./lib/type/str')
42+
};
43+
2744
// Removed functions from JS-YAML 3.0.x
2845
module.exports.safeLoad = renamed('safeLoad', 'load');
2946
module.exports.safeLoadAll = renamed('safeLoadAll', 'loadAll');

lib/schema.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ var YAMLException = require('./exception');
66
var Type = require('./type');
77

88

9-
function compileList(schema, name, result) {
10-
var exclude = [];
9+
function compileList(schema, name) {
10+
var result = [];
1111

1212
schema[name].forEach(function (currentType) {
13+
var newIndex = result.length;
14+
1315
result.forEach(function (previousType, previousIndex) {
1416
if (previousType.tag === currentType.tag &&
1517
previousType.kind === currentType.kind &&
1618
previousType.multi === currentType.multi) {
1719

18-
exclude.push(previousIndex);
20+
newIndex = previousIndex;
1921
}
2022
});
2123

22-
result.push(currentType);
24+
result[newIndex] = currentType;
2325
});
2426

25-
return result.filter(function (type, index) {
26-
return exclude.indexOf(index) === -1;
27-
});
27+
return result;
2828
}
2929

3030

@@ -110,8 +110,8 @@ Schema.prototype.extend = function extend(definition) {
110110
result.implicit = (this.implicit || []).concat(implicit);
111111
result.explicit = (this.explicit || []).concat(explicit);
112112

113-
result.compiledImplicit = compileList(result, 'implicit', []);
114-
result.compiledExplicit = compileList(result, 'explicit', []);
113+
result.compiledImplicit = compileList(result, 'implicit');
114+
result.compiledExplicit = compileList(result, 'explicit');
115115
result.compiledTypeMap = compileMap(result.compiledImplicit, result.compiledExplicit);
116116

117117
return result;

lib/type.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ function Type(tag, options) {
4545
});
4646

4747
// TODO: Add tag format check.
48+
this.options = options; // keep original options in case user wants to extend this type later
4849
this.tag = tag;
4950
this.kind = options['kind'] || null;
5051
this.resolve = options['resolve'] || function () { return true; };

test/.eslintrc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ env:
44
es6: true
55

66
parserOptions:
7-
ecmaVersion: 2017
7+
ecmaVersion: 2020
88

99
rules:
1010
no-undefined: 0

test/issues/0586.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict';
2+
3+
/* eslint-disable no-use-before-define, new-cap */
4+
5+
6+
const assert = require('assert');
7+
const yaml = require('../../');
8+
9+
10+
it('Should allow custom formatting through implicit custom tags', function () {
11+
function CustomDump(data, opts) {
12+
if (!(this instanceof CustomDump)) return new CustomDump(data, opts);
13+
this.data = data;
14+
this.opts = opts;
15+
}
16+
17+
CustomDump.prototype.represent = function () {
18+
let result = yaml.dump(this.data, Object.assign({ replacer, schema }, this.opts));
19+
result = result.trim();
20+
if (result.includes('\n')) result = '\n' + result;
21+
return result;
22+
};
23+
24+
25+
let CustomDumpType = new yaml.Type('!format', {
26+
kind: 'scalar',
27+
resolve: () => false,
28+
instanceOf: CustomDump,
29+
represent: d => d.represent()
30+
});
31+
32+
33+
let schema = yaml.DEFAULT_SCHEMA.extend({ implicit: [ CustomDumpType ] });
34+
35+
function replacer(key, value) {
36+
if (key === '') return value; // top-level, don't change this
37+
if (key === 'flow_choices') return CustomDump(value, { flowLevel: 0 });
38+
if (key === 'block_choices') return CustomDump(value, { flowLevel: Infinity });
39+
return value; // default
40+
}
41+
42+
let result = CustomDump({ flow_choices : [ 1, 2 ], block_choices: [ 4, 5 ] }).represent().trim();
43+
44+
assert.strictEqual(result, `
45+
flow_choices: [1, 2]
46+
block_choices:
47+
- 4
48+
- 5`.replace(/^\n/, ''));
49+
});

test/issues/0614.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
/* global BigInt */
4+
5+
6+
const assert = require('assert');
7+
const yaml = require('../../');
8+
9+
10+
it('Should allow int override', function () {
11+
let options = Object.assign({}, yaml.types.int.options);
12+
13+
options.construct = data => {
14+
let value = data, sign = 1n, ch;
15+
16+
if (value.indexOf('_') !== -1) {
17+
value = value.replace(/_/g, '');
18+
}
19+
20+
ch = value[0];
21+
22+
if (ch === '-' || ch === '+') {
23+
if (ch === '-') sign = -1n;
24+
value = value.slice(1);
25+
ch = value[0];
26+
}
27+
28+
return sign * BigInt(value);
29+
};
30+
31+
32+
let BigIntType = new yaml.Type('tag:yaml.org,2002:int', options);
33+
34+
const SCHEMA = yaml.DEFAULT_SCHEMA.extend({ implicit: [ BigIntType ] });
35+
36+
const data = `
37+
int: -123_456_789
38+
bigint: -12_345_678_901_234_567_890
39+
float: -12_345_678_901_234_567_890.1234
40+
`;
41+
42+
assert.deepStrictEqual(yaml.load(data, { schema: SCHEMA }), {
43+
int: -123456789n,
44+
bigint: -12345678901234567890n,
45+
float: -12345678901234567000 // precision loss expected
46+
});
47+
});

0 commit comments

Comments
 (0)