1 | 'use strict';
|
2 |
|
3 | var Alias = require('../nodes/Alias.js');
|
4 | var Collection = require('../nodes/Collection.js');
|
5 | var identity = require('../nodes/identity.js');
|
6 | var Pair = require('../nodes/Pair.js');
|
7 | var toJS = require('../nodes/toJS.js');
|
8 | var Schema = require('../schema/Schema.js');
|
9 | var stringifyDocument = require('../stringify/stringifyDocument.js');
|
10 | var anchors = require('./anchors.js');
|
11 | var applyReviver = require('./applyReviver.js');
|
12 | var createNode = require('./createNode.js');
|
13 | var directives = require('./directives.js');
|
14 |
|
15 | class Document {
|
16 | constructor(value, replacer, options) {
|
17 |
|
18 | this.commentBefore = null;
|
19 |
|
20 | this.comment = null;
|
21 |
|
22 | this.errors = [];
|
23 |
|
24 | this.warnings = [];
|
25 | Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC });
|
26 | let _replacer = null;
|
27 | if (typeof replacer === 'function' || Array.isArray(replacer)) {
|
28 | _replacer = replacer;
|
29 | }
|
30 | else if (options === undefined && replacer) {
|
31 | options = replacer;
|
32 | replacer = undefined;
|
33 | }
|
34 | const opt = Object.assign({
|
35 | intAsBigInt: false,
|
36 | keepSourceTokens: false,
|
37 | logLevel: 'warn',
|
38 | prettyErrors: true,
|
39 | strict: true,
|
40 | uniqueKeys: true,
|
41 | version: '1.2'
|
42 | }, options);
|
43 | this.options = opt;
|
44 | let { version } = opt;
|
45 | if (options?._directives) {
|
46 | this.directives = options._directives.atDocument();
|
47 | if (this.directives.yaml.explicit)
|
48 | version = this.directives.yaml.version;
|
49 | }
|
50 | else
|
51 | this.directives = new directives.Directives({ version });
|
52 | this.setSchema(version, options);
|
53 |
|
54 | this.contents =
|
55 | value === undefined ? null : this.createNode(value, _replacer, options);
|
56 | }
|
57 | |
58 |
|
59 |
|
60 |
|
61 |
|
62 | clone() {
|
63 | const copy = Object.create(Document.prototype, {
|
64 | [identity.NODE_TYPE]: { value: identity.DOC }
|
65 | });
|
66 | copy.commentBefore = this.commentBefore;
|
67 | copy.comment = this.comment;
|
68 | copy.errors = this.errors.slice();
|
69 | copy.warnings = this.warnings.slice();
|
70 | copy.options = Object.assign({}, this.options);
|
71 | if (this.directives)
|
72 | copy.directives = this.directives.clone();
|
73 | copy.schema = this.schema.clone();
|
74 |
|
75 | copy.contents = identity.isNode(this.contents)
|
76 | ? this.contents.clone(copy.schema)
|
77 | : this.contents;
|
78 | if (this.range)
|
79 | copy.range = this.range.slice();
|
80 | return copy;
|
81 | }
|
82 |
|
83 | add(value) {
|
84 | if (assertCollection(this.contents))
|
85 | this.contents.add(value);
|
86 | }
|
87 |
|
88 | addIn(path, value) {
|
89 | if (assertCollection(this.contents))
|
90 | this.contents.addIn(path, value);
|
91 | }
|
92 | |
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 | createAlias(node, name) {
|
102 | if (!node.anchor) {
|
103 | const prev = anchors.anchorNames(this);
|
104 | node.anchor =
|
105 |
|
106 | !name || prev.has(name) ? anchors.findNewAnchor(name || 'a', prev) : name;
|
107 | }
|
108 | return new Alias.Alias(node.anchor);
|
109 | }
|
110 | createNode(value, replacer, options) {
|
111 | let _replacer = undefined;
|
112 | if (typeof replacer === 'function') {
|
113 | value = replacer.call({ '': value }, '', value);
|
114 | _replacer = replacer;
|
115 | }
|
116 | else if (Array.isArray(replacer)) {
|
117 | const keyToStr = (v) => typeof v === 'number' || v instanceof String || v instanceof Number;
|
118 | const asStr = replacer.filter(keyToStr).map(String);
|
119 | if (asStr.length > 0)
|
120 | replacer = replacer.concat(asStr);
|
121 | _replacer = replacer;
|
122 | }
|
123 | else if (options === undefined && replacer) {
|
124 | options = replacer;
|
125 | replacer = undefined;
|
126 | }
|
127 | const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {};
|
128 | const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors(this,
|
129 |
|
130 | anchorPrefix || 'a');
|
131 | const ctx = {
|
132 | aliasDuplicateObjects: aliasDuplicateObjects ?? true,
|
133 | keepUndefined: keepUndefined ?? false,
|
134 | onAnchor,
|
135 | onTagObj,
|
136 | replacer: _replacer,
|
137 | schema: this.schema,
|
138 | sourceObjects
|
139 | };
|
140 | const node = createNode.createNode(value, tag, ctx);
|
141 | if (flow && identity.isCollection(node))
|
142 | node.flow = true;
|
143 | setAnchors();
|
144 | return node;
|
145 | }
|
146 | |
147 |
|
148 |
|
149 |
|
150 | createPair(key, value, options = {}) {
|
151 | const k = this.createNode(key, null, options);
|
152 | const v = this.createNode(value, null, options);
|
153 | return new Pair.Pair(k, v);
|
154 | }
|
155 | |
156 |
|
157 |
|
158 |
|
159 | delete(key) {
|
160 | return assertCollection(this.contents) ? this.contents.delete(key) : false;
|
161 | }
|
162 | |
163 |
|
164 |
|
165 |
|
166 | deleteIn(path) {
|
167 | if (Collection.isEmptyPath(path)) {
|
168 | if (this.contents == null)
|
169 | return false;
|
170 |
|
171 | this.contents = null;
|
172 | return true;
|
173 | }
|
174 | return assertCollection(this.contents)
|
175 | ? this.contents.deleteIn(path)
|
176 | : false;
|
177 | }
|
178 | |
179 |
|
180 |
|
181 |
|
182 |
|
183 | get(key, keepScalar) {
|
184 | return identity.isCollection(this.contents)
|
185 | ? this.contents.get(key, keepScalar)
|
186 | : undefined;
|
187 | }
|
188 | |
189 |
|
190 |
|
191 |
|
192 |
|
193 | getIn(path, keepScalar) {
|
194 | if (Collection.isEmptyPath(path))
|
195 | return !keepScalar && identity.isScalar(this.contents)
|
196 | ? this.contents.value
|
197 | : this.contents;
|
198 | return identity.isCollection(this.contents)
|
199 | ? this.contents.getIn(path, keepScalar)
|
200 | : undefined;
|
201 | }
|
202 | |
203 |
|
204 |
|
205 | has(key) {
|
206 | return identity.isCollection(this.contents) ? this.contents.has(key) : false;
|
207 | }
|
208 | |
209 |
|
210 |
|
211 | hasIn(path) {
|
212 | if (Collection.isEmptyPath(path))
|
213 | return this.contents !== undefined;
|
214 | return identity.isCollection(this.contents) ? this.contents.hasIn(path) : false;
|
215 | }
|
216 | |
217 |
|
218 |
|
219 |
|
220 | set(key, value) {
|
221 | if (this.contents == null) {
|
222 |
|
223 | this.contents = Collection.collectionFromPath(this.schema, [key], value);
|
224 | }
|
225 | else if (assertCollection(this.contents)) {
|
226 | this.contents.set(key, value);
|
227 | }
|
228 | }
|
229 | |
230 |
|
231 |
|
232 |
|
233 | setIn(path, value) {
|
234 | if (Collection.isEmptyPath(path)) {
|
235 |
|
236 | this.contents = value;
|
237 | }
|
238 | else if (this.contents == null) {
|
239 |
|
240 | this.contents = Collection.collectionFromPath(this.schema, Array.from(path), value);
|
241 | }
|
242 | else if (assertCollection(this.contents)) {
|
243 | this.contents.setIn(path, value);
|
244 | }
|
245 | }
|
246 | |
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 | setSchema(version, options = {}) {
|
254 | if (typeof version === 'number')
|
255 | version = String(version);
|
256 | let opt;
|
257 | switch (version) {
|
258 | case '1.1':
|
259 | if (this.directives)
|
260 | this.directives.yaml.version = '1.1';
|
261 | else
|
262 | this.directives = new directives.Directives({ version: '1.1' });
|
263 | opt = { merge: true, resolveKnownTags: false, schema: 'yaml-1.1' };
|
264 | break;
|
265 | case '1.2':
|
266 | case 'next':
|
267 | if (this.directives)
|
268 | this.directives.yaml.version = version;
|
269 | else
|
270 | this.directives = new directives.Directives({ version });
|
271 | opt = { merge: false, resolveKnownTags: true, schema: 'core' };
|
272 | break;
|
273 | case null:
|
274 | if (this.directives)
|
275 | delete this.directives;
|
276 | opt = null;
|
277 | break;
|
278 | default: {
|
279 | const sv = JSON.stringify(version);
|
280 | throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`);
|
281 | }
|
282 | }
|
283 |
|
284 | if (options.schema instanceof Object)
|
285 | this.schema = options.schema;
|
286 | else if (opt)
|
287 | this.schema = new Schema.Schema(Object.assign(opt, options));
|
288 | else
|
289 | throw new Error(`With a null YAML version, the { schema: Schema } option is required`);
|
290 | }
|
291 |
|
292 | toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) {
|
293 | const ctx = {
|
294 | anchors: new Map(),
|
295 | doc: this,
|
296 | keep: !json,
|
297 | mapAsMap: mapAsMap === true,
|
298 | mapKeyWarned: false,
|
299 | maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100
|
300 | };
|
301 | const res = toJS.toJS(this.contents, jsonArg ?? '', ctx);
|
302 | if (typeof onAnchor === 'function')
|
303 | for (const { count, res } of ctx.anchors.values())
|
304 | onAnchor(res, count);
|
305 | return typeof reviver === 'function'
|
306 | ? applyReviver.applyReviver(reviver, { '': res }, '', res)
|
307 | : res;
|
308 | }
|
309 | |
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 | toJSON(jsonArg, onAnchor) {
|
316 | return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor });
|
317 | }
|
318 |
|
319 | toString(options = {}) {
|
320 | if (this.errors.length > 0)
|
321 | throw new Error('Document with errors cannot be stringified');
|
322 | if ('indent' in options &&
|
323 | (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) {
|
324 | const s = JSON.stringify(options.indent);
|
325 | throw new Error(`"indent" option must be a positive integer, not ${s}`);
|
326 | }
|
327 | return stringifyDocument.stringifyDocument(this, options);
|
328 | }
|
329 | }
|
330 | function assertCollection(contents) {
|
331 | if (identity.isCollection(contents))
|
332 | return true;
|
333 | throw new Error('Expected a YAML collection as document contents');
|
334 | }
|
335 |
|
336 | exports.Document = Document;
|