forked from microsoft/rushstack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJsonFile.ts
More file actions
103 lines (87 loc) · 3.52 KB
/
JsonFile.ts
File metadata and controls
103 lines (87 loc) · 3.52 KB
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
103
import * as fsx from 'fs-extra';
import * as os from 'os';
import * as jju from 'jju';
import Validator = require('z-schema');
export type ValidateErrorCallback = (errorDescription: string) => void;
/**
* Utilities for reading/writing JSON files.
*/
export default class JsonFile {
public static validateSchema(jsonObject: Object, jsonSchemaObject: Object,
errorCallback: ValidateErrorCallback): void {
// Remove the $schema reference that appears in the configuration object (used for IntelliSense),
// since we are replacing it with the precompiled version. The validator.setRemoteReference()
// API is a better way to handle this, but we'd first need to publish the schema file
// to a public web server where Visual Studio can find it.
// tslint:disable-next-line:no-string-literal
delete jsonSchemaObject['$schema'];
const validator: Validator = new Validator({
breakOnFirstError: false,
noTypeless: true
});
if (!validator.validate(jsonObject, jsonSchemaObject)) {
const errorDetails: Validator.SchemaErrorDetail[] = validator.getLastErrors();
let buffer: string = 'JSON schema validation failed:';
buffer = JsonFile._formatErrorDetails(errorDetails, ' ', buffer);
errorCallback(buffer);
}
}
public static loadJsonFile(jsonFilename: string): {} {
if (!fsx.existsSync(jsonFilename)) {
throw new Error(`Input file not found: ${jsonFilename}`);
}
const buffer: Buffer = fsx.readFileSync(jsonFilename);
try {
return jju.parse(buffer.toString());
} catch (error) {
throw new Error(`Error reading "${jsonFilename}":` + os.EOL + ` ${error.message}`);
}
}
public static saveJsonFile(jsonFilename: string, jsonData: {}): void {
JsonFile._validateNoUndefinedMembers(jsonData);
const stringified: string = JSON.stringify(jsonData, undefined, 2) + '\n';
const normalized: string = JsonFile._getAllReplaced(stringified, '\n', '\r\n');
fsx.writeFileSync(jsonFilename, normalized);
}
/**
* Used to validate a data structure before writing. Reports an error if there
* are any undefined members.
*/
// tslint:disable-next-line:no-any
private static _validateNoUndefinedMembers(json: any): void {
if (!json) {
return;
}
if (typeof json === 'object') {
for (const key of Object.keys(json)) {
// tslint:disable-next-line:no-any
const value: any = json[key];
if (value === undefined) {
throw new Error(`The key "${key}" is undefined`);
}
JsonFile._validateNoUndefinedMembers(value);
}
}
}
private static _formatErrorDetails(errorDetails: Validator.SchemaErrorDetail[], indent: string,
buffer: string): string {
for (const errorDetail of errorDetails) {
buffer += os.EOL + indent + `Error: ${errorDetail.path}`;
buffer += os.EOL + indent + ` ${errorDetail.message}`;
if (errorDetail.inner) {
buffer = JsonFile._formatErrorDetails(errorDetail.inner, indent + ' ', buffer);
}
}
return buffer;
}
/**
* Returns the same thing as targetString.replace(searchValue, replaceValue), except that
* all matches are replaced, rather than just the first match.
* @param targetString The string to be modified
* @param searchValue The value to search for
* @param replaceValue The replacement text
*/
private static _getAllReplaced(targetString: string, searchValue: string, replaceValue: string): string {
return targetString.split(searchValue).join(replaceValue);
}
}