/
syml.ts
102 lines (84 loc) Β· 2.78 KB
/
syml.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// @ts-ignore
import {safeLoad} from 'js-yaml';
import {parse} from './grammars/syml';
const simpleStringPattern = /^(?![-?:,\][{}#&*!|>'"%@` \t\r\n]).([ \t]*(?![,\][{}:# \t\r\n]).)*$/;
// The following keys will always be stored at the top of the object, in the
// specified order. It's not fair but life isn't fair either.
const specialObjectKeys = [`__metadata`, `version`, `resolution`, `dependencies`, `peerDependencies`, `dependenciesMeta`, `peerDependenciesMeta`];
function stringifyString(value: string): string {
if (value.match(simpleStringPattern)) {
return value;
} else {
return JSON.stringify(value);
}
}
function stringifyValue(value: any, indentLevel: number): string {
if (value === null) {
if (indentLevel === 0) {
throw new Error(`Null is not a valid top-level value`);
} else {
return ` null`;
}
}
if (typeof value === `number` || typeof value === `boolean`) {
if (indentLevel === 0) {
return `${value.toString()}\n`;
} else {
return ` ${value.toString()}`;
}
}
if (typeof value === `string`) {
if (indentLevel === 0) {
return `${stringifyString(value)}\n`;
} else {
return ` ${stringifyString(value)}`;
}
}
if (Array.isArray(value)) {
const indent = ` `.repeat(indentLevel);
return value.map(sub => {
return `\n${indent}-${stringifyValue(sub, indentLevel + 1)}`;
}).join(``);
}
if (typeof value === `object` && value) {
const indent = ` `.repeat(indentLevel);
const keys = Object.keys(value).sort((a, b) => {
const aIndex = specialObjectKeys.indexOf(a);
const bIndex = specialObjectKeys.indexOf(b);
if (aIndex === -1 && bIndex === -1)
return a < b ? -1 : a > b ? +1 : 0;
if (aIndex !== -1 && bIndex === -1)
return -1;
if (aIndex === -1 && bIndex !== -1)
return +1;
return aIndex - bIndex;
});
const fields = keys.filter(key => {
return value[key] !== undefined;
}).map(key => {
return `${indent}${stringifyString(key)}:${stringifyValue(value[key], indentLevel + 1)}`;
}).join(indentLevel === 0 ? `\n\n` : `\n`);
if (indentLevel === 0) {
return fields ? `${fields}\n` : ``;
} else {
return `\n${fields}`;
}
}
throw new Error(`Unsupported value type (${value})`);
}
export function stringifySyml(value: any) {
return stringifyValue(value, 0);
}
export function parseSyml(source: string) {
try {
try {
return safeLoad(source) as {[key: string]: any};
} catch (error) {
return parse(source.endsWith(`\n`) ? source : `${source}\n`);
}
} catch (error) {
if (error.location)
error.message = error.message.replace(/(\.)?$/, ` (line ${error.location.start.line}, column ${error.location.start.column})$1`);
throw error;
}
}