1 line
29 KiB
Plaintext
1 line
29 KiB
Plaintext
{"version":3,"file":"JsonFile.js","sourceRoot":"","sources":["../src/JsonFile.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport * as os from 'os';\nimport * as jju from 'jju';\n\nimport type { JsonSchema, IJsonSchemaErrorInfo, IJsonSchemaValidateOptions } from './JsonSchema';\nimport { Text, type NewlineKind } from './Text';\nimport { FileSystem } from './FileSystem';\n\n/**\n * Represents a JSON-serializable object whose type has not been determined yet.\n *\n * @remarks\n *\n * This type is similar to `any`, except that it communicates that the object is serializable JSON.\n *\n * @public\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type JsonObject = any;\n\n/**\n * The Rush Stack lint rules discourage usage of `null`. However, JSON parsers always return JavaScript's\n * `null` to keep the two syntaxes consistent. When creating interfaces that describe JSON structures,\n * use `JsonNull` to avoid triggering the lint rule. Do not use `JsonNull` for any other purpose.\n *\n * @remarks\n * If you are designing a new JSON file format, it's a good idea to avoid `null` entirely. In most cases\n * there are better representations that convey more information about an item that is unknown, omitted, or disabled.\n *\n * To understand why `null` is deprecated, please see the `@rushstack/eslint-plugin` documentation here:\n *\n * {@link https://www.npmjs.com/package/@rushstack/eslint-plugin#rushstackno-null}\n *\n * @public\n */\n// eslint-disable-next-line @rushstack/no-new-null\nexport type JsonNull = null;\n\n/**\n * Specifies the variant of JSON syntax to be used.\n *\n * @public\n */\nexport enum JsonSyntax {\n /**\n * Specifies the exact RFC 8259 format as implemented by the `JSON.parse()` system API.\n * This format was designed for machine generated inputs such as an HTTP payload.\n * It is not a recommend choice for human-authored files, because it does not support\n * code comments.\n *\n * @remarks\n *\n * A well-known quote from Douglas Crockford, the inventor of JSON:\n *\n * \"I removed comments from JSON because I saw people were using them to hold parsing directives,\n * a practice which would have destroyed interoperability. I know that the lack of comments makes\n * some people sad, but it shouldn't. Suppose you are using JSON to keep configuration files,\n * which you would like to annotate. Go ahead and insert all the comments you like.\n * Then pipe it through JSMin before handing it to your JSON parser.\"\n *\n * @see {@link https://datatracker.ietf.org/doc/html/rfc8259 | RFC 8259}\n */\n Strict = 'strict',\n\n /**\n * `JsonSyntax.JsonWithComments` is the recommended format for human-authored config files.\n * It is a minimal extension to `JsonSyntax.Strict` adding support for code comments\n * using `//` and `/*`.\n *\n * @remarks\n *\n * VS Code calls this format `jsonc`, but it should not be confused with unrelated file formats\n * and libraries that also use the name \"JSONC\".\n *\n * To fix VS Code syntax highlighting, add this setting:\n * `\"files.associations\": { \"*.json\": \"jsonc\" }`\n *\n * To fix GitHub syntax highlighting, add this to your `.gitattributes`:\n * `*.json linguist-language=JSON-with-Comments`\n */\n JsonWithComments = 'jsonWithComments',\n\n /**\n * JSON5 is a project that proposes a JSON-like format supplemented with ECMAScript 5.1\n * notations for objects, numbers, comments, and more.\n *\n * @remarks\n * Files using this format should use the `.json5` file extension instead of `.json`.\n *\n * JSON5 has substantial differences from JSON: object keys may be unquoted, trailing commas\n * are allowed, and strings may span multiple lines. Whereas `JsonSyntax.JsonWithComments` can\n * be cheaply converted to standard JSON by stripping comments, parsing JSON5 requires a\n * nontrivial algorithm that may not be easily available in some contexts or programming languages.\n *\n * @see {@link https://json5.org/ | JSON5 project website}\n */\n Json5 = 'json5'\n}\n\n/**\n * Options for {@link JsonFile.parseString}, {@link JsonFile.load}, and {@link JsonFile.loadAsync}.\n *\n * @public\n */\nexport interface IJsonFileParseOptions {\n /**\n * Specifies the variant of JSON syntax to be used.\n *\n * @defaultValue\n * `JsonSyntax.Json5`\n *\n * NOTE: This default will be changed to `JsonSyntax.JsonWithComments` in a future release.\n */\n jsonSyntax?: JsonSyntax;\n}\n\n/**\n * Options for {@link JsonFile.loadAndValidate} and {@link JsonFile.loadAndValidateAsync}\n *\n * @public\n */\nexport interface IJsonFileLoadAndValidateOptions extends IJsonFileParseOptions, IJsonSchemaValidateOptions {}\n\n/**\n * Options for {@link JsonFile.stringify}\n *\n * @public\n */\nexport interface IJsonFileStringifyOptions {\n /**\n * If provided, the specified newline type will be used instead of the default `\\r\\n`.\n */\n newlineConversion?: NewlineKind;\n\n /**\n * By default, `JsonFile.stringify()` validates that the object does not contain any\n * keys whose value is `undefined`. To disable this validation, set `ignoreUndefinedValues=true`\n * which causes such keys to be silently discarded, consistent with the system `JSON.stringify()`.\n *\n * @remarks\n *\n * The JSON file format can represent `null` values ({@link JsonNull}) but not `undefined` values.\n * In ECMAScript code however, we generally avoid `null` and always represent empty states\n * as `undefined`, because it is the default value of missing/uninitialized variables.\n * (In practice, distinguishing \"null\" versus \"uninitialized\" has more drawbacks than benefits.)\n * This poses a problem when serializing ECMAScript objects that contain `undefined` members.\n * As a safeguard, `JsonFile` will report an error if any `undefined` values are encountered\n * during serialization. Set `ignoreUndefinedValues=true` to disable this safeguard.\n */\n ignoreUndefinedValues?: boolean;\n\n /**\n * If true, then the \"jju\" library will be used to improve the text formatting.\n * Note that this is slightly slower than the native JSON.stringify() implementation.\n */\n prettyFormatting?: boolean;\n\n /**\n * If specified, this header will be prepended to the start of the file. The header must consist\n * of lines prefixed by \"//\" characters.\n * @remarks\n * When used with {@link IJsonFileSaveOptions.updateExistingFile}\n * or {@link JsonFile.updateString}, the header will ONLY be added for a newly created file.\n */\n headerComment?: string;\n}\n\n/**\n * Options for {@link JsonFile.save} and {@link JsonFile.saveAsync}.\n *\n * @public\n */\nexport interface IJsonFileSaveOptions extends IJsonFileStringifyOptions {\n /**\n * If there is an existing file, and the contents have not changed, then\n * don't write anything; this preserves the old timestamp.\n */\n onlyIfChanged?: boolean;\n\n /**\n * Creates the folder recursively using FileSystem.ensureFolder()\n * Defaults to false.\n */\n ensureFolderExists?: boolean;\n\n /**\n * If true, use the \"jju\" library to preserve the existing JSON formatting: The file will be loaded\n * from the target filename, the new content will be merged in (preserving whitespace and comments),\n * and then the file will be overwritten with the merged contents. If the target file does not exist,\n * then the file is saved normally.\n */\n updateExistingFile?: boolean;\n}\n\nconst DEFAULT_ENCODING: 'utf8' = 'utf8';\n\n/**\n * Utilities for reading/writing JSON files.\n * @public\n */\nexport class JsonFile {\n /**\n * @internal\n */\n public static _formatPathForError: (path: string) => string = (path: string) => path;\n\n /**\n * Loads a JSON file.\n */\n public static load(jsonFilename: string, options?: IJsonFileParseOptions): JsonObject {\n try {\n const contents: string = FileSystem.readFile(jsonFilename);\n const parseOptions: jju.ParseOptions = JsonFile._buildJjuParseOptions(options);\n return jju.parse(contents, parseOptions);\n } catch (error) {\n if (FileSystem.isNotExistError(error as Error)) {\n throw error;\n } else {\n throw new Error(\n `Error reading \"${JsonFile._formatPathForError(jsonFilename)}\":` +\n os.EOL +\n ` ${(error as Error).message}`\n );\n }\n }\n }\n\n /**\n * An async version of {@link JsonFile.load}.\n */\n public static async loadAsync(jsonFilename: string, options?: IJsonFileParseOptions): Promise<JsonObject> {\n try {\n const contents: string = await FileSystem.readFileAsync(jsonFilename);\n const parseOptions: jju.ParseOptions = JsonFile._buildJjuParseOptions(options);\n return jju.parse(contents, parseOptions);\n } catch (error) {\n if (FileSystem.isNotExistError(error as Error)) {\n throw error;\n } else {\n throw new Error(\n `Error reading \"${JsonFile._formatPathForError(jsonFilename)}\":` +\n os.EOL +\n ` ${(error as Error).message}`\n );\n }\n }\n }\n\n /**\n * Parses a JSON file's contents.\n */\n public static parseString(jsonContents: string, options?: IJsonFileParseOptions): JsonObject {\n const parseOptions: jju.ParseOptions = JsonFile._buildJjuParseOptions(options);\n return jju.parse(jsonContents, parseOptions);\n }\n\n /**\n * Loads a JSON file and validate its schema.\n */\n public static loadAndValidate(\n jsonFilename: string,\n jsonSchema: JsonSchema,\n options?: IJsonFileLoadAndValidateOptions\n ): JsonObject {\n const jsonObject: JsonObject = JsonFile.load(jsonFilename, options);\n jsonSchema.validateObject(jsonObject, jsonFilename, options);\n\n return jsonObject;\n }\n\n /**\n * An async version of {@link JsonFile.loadAndValidate}.\n */\n public static async loadAndValidateAsync(\n jsonFilename: string,\n jsonSchema: JsonSchema,\n options?: IJsonFileLoadAndValidateOptions\n ): Promise<JsonObject> {\n const jsonObject: JsonObject = await JsonFile.loadAsync(jsonFilename, options);\n jsonSchema.validateObject(jsonObject, jsonFilename, options);\n\n return jsonObject;\n }\n\n /**\n * Loads a JSON file and validate its schema, reporting errors using a callback\n * @remarks\n * See JsonSchema.validateObjectWithCallback() for more info.\n */\n public static loadAndValidateWithCallback(\n jsonFilename: string,\n jsonSchema: JsonSchema,\n errorCallback: (errorInfo: IJsonSchemaErrorInfo) => void,\n options?: IJsonFileLoadAndValidateOptions\n ): JsonObject {\n const jsonObject: JsonObject = JsonFile.load(jsonFilename, options);\n jsonSchema.validateObjectWithCallback(jsonObject, errorCallback);\n\n return jsonObject;\n }\n\n /**\n * An async version of {@link JsonFile.loadAndValidateWithCallback}.\n */\n public static async loadAndValidateWithCallbackAsync(\n jsonFilename: string,\n jsonSchema: JsonSchema,\n errorCallback: (errorInfo: IJsonSchemaErrorInfo) => void,\n options?: IJsonFileLoadAndValidateOptions\n ): Promise<JsonObject> {\n const jsonObject: JsonObject = await JsonFile.loadAsync(jsonFilename, options);\n jsonSchema.validateObjectWithCallback(jsonObject, errorCallback);\n\n return jsonObject;\n }\n\n /**\n * Serializes the specified JSON object to a string buffer.\n * @param jsonObject - the object to be serialized\n * @param options - other settings that control serialization\n * @returns a JSON string, with newlines, and indented with two spaces\n */\n public static stringify(jsonObject: JsonObject, options?: IJsonFileStringifyOptions): string {\n return JsonFile.updateString('', jsonObject, options);\n }\n\n /**\n * Serializes the specified JSON object to a string buffer.\n * @param previousJson - the previous JSON string, which will be updated\n * @param newJsonObject - the object to be serialized\n * @param options - other settings that control serialization\n * @returns a JSON string, with newlines, and indented with two spaces\n */\n public static updateString(\n previousJson: string,\n newJsonObject: JsonObject,\n options?: IJsonFileStringifyOptions\n ): string {\n if (!options) {\n options = {};\n }\n\n if (!options.ignoreUndefinedValues) {\n // Standard handling of `undefined` in JSON stringification is to discard the key.\n JsonFile.validateNoUndefinedMembers(newJsonObject);\n }\n\n let stringified: string;\n\n if (previousJson !== '') {\n // NOTE: We don't use mode=json here because comments aren't allowed by strict JSON\n stringified = jju.update(previousJson, newJsonObject, {\n mode: 'cjson',\n indent: 2\n });\n } else if (options.prettyFormatting) {\n stringified = jju.stringify(newJsonObject, {\n mode: 'json',\n indent: 2\n });\n\n if (options.headerComment !== undefined) {\n stringified = JsonFile._formatJsonHeaderComment(options.headerComment) + stringified;\n }\n } else {\n stringified = JSON.stringify(newJsonObject, undefined, 2);\n\n if (options.headerComment !== undefined) {\n stringified = JsonFile._formatJsonHeaderComment(options.headerComment) + stringified;\n }\n }\n\n // Add the trailing newline\n stringified = Text.ensureTrailingNewline(stringified);\n\n if (options && options.newlineConversion) {\n stringified = Text.convertTo(stringified, options.newlineConversion);\n }\n\n return stringified;\n }\n\n /**\n * Saves the file to disk. Returns false if nothing was written due to options.onlyIfChanged.\n * @param jsonObject - the object to be saved\n * @param jsonFilename - the file path to write\n * @param options - other settings that control how the file is saved\n * @returns false if ISaveJsonFileOptions.onlyIfChanged didn't save anything; true otherwise\n */\n public static save(jsonObject: JsonObject, jsonFilename: string, options?: IJsonFileSaveOptions): boolean {\n if (!options) {\n options = {};\n }\n\n // Do we need to read the previous file contents?\n let oldBuffer: Buffer | undefined = undefined;\n if (options.updateExistingFile || options.onlyIfChanged) {\n try {\n oldBuffer = FileSystem.readFileToBuffer(jsonFilename);\n } catch (error) {\n if (!FileSystem.isNotExistError(error as Error)) {\n throw error;\n }\n }\n }\n\n let jsonToUpdate: string = '';\n if (options.updateExistingFile && oldBuffer) {\n jsonToUpdate = oldBuffer.toString(DEFAULT_ENCODING);\n }\n\n const newJson: string = JsonFile.updateString(jsonToUpdate, jsonObject, options);\n\n const newBuffer: Buffer = Buffer.from(newJson, DEFAULT_ENCODING);\n\n if (options.onlyIfChanged) {\n // Has the file changed?\n if (oldBuffer && Buffer.compare(newBuffer, oldBuffer) === 0) {\n // Nothing has changed, so don't touch the file\n return false;\n }\n }\n\n FileSystem.writeFile(jsonFilename, newBuffer, {\n ensureFolderExists: options.ensureFolderExists\n });\n\n // TEST CODE: Used to verify that onlyIfChanged isn't broken by a hidden transformation during saving.\n /*\n const oldBuffer2: Buffer = FileSystem.readFileToBuffer(jsonFilename);\n if (Buffer.compare(buffer, oldBuffer2) !== 0) {\n console.log('new:' + buffer.toString('hex'));\n console.log('old:' + oldBuffer2.toString('hex'));\n\n throw new Error('onlyIfChanged logic is broken');\n }\n */\n return true;\n }\n\n /**\n * An async version of {@link JsonFile.save}.\n */\n public static async saveAsync(\n jsonObject: JsonObject,\n jsonFilename: string,\n options?: IJsonFileSaveOptions\n ): Promise<boolean> {\n if (!options) {\n options = {};\n }\n\n // Do we need to read the previous file contents?\n let oldBuffer: Buffer | undefined = undefined;\n if (options.updateExistingFile || options.onlyIfChanged) {\n try {\n oldBuffer = await FileSystem.readFileToBufferAsync(jsonFilename);\n } catch (error) {\n if (!FileSystem.isNotExistError(error as Error)) {\n throw error;\n }\n }\n }\n\n let jsonToUpdate: string = '';\n if (options.updateExistingFile && oldBuffer) {\n jsonToUpdate = oldBuffer.toString(DEFAULT_ENCODING);\n }\n\n const newJson: string = JsonFile.updateString(jsonToUpdate, jsonObject, options);\n\n const newBuffer: Buffer = Buffer.from(newJson, DEFAULT_ENCODING);\n\n if (options.onlyIfChanged) {\n // Has the file changed?\n if (oldBuffer && Buffer.compare(newBuffer, oldBuffer) === 0) {\n // Nothing has changed, so don't touch the file\n return false;\n }\n }\n\n await FileSystem.writeFileAsync(jsonFilename, newBuffer, {\n ensureFolderExists: options.ensureFolderExists\n });\n\n // TEST CODE: Used to verify that onlyIfChanged isn't broken by a hidden transformation during saving.\n /*\n const oldBuffer2: Buffer = await FileSystem.readFileToBufferAsync(jsonFilename);\n if (Buffer.compare(buffer, oldBuffer2) !== 0) {\n console.log('new:' + buffer.toString('hex'));\n console.log('old:' + oldBuffer2.toString('hex'));\n\n throw new Error('onlyIfChanged logic is broken');\n }\n */\n return true;\n }\n\n /**\n * Used to validate a data structure before writing. Reports an error if there\n * are any undefined members.\n */\n public static validateNoUndefinedMembers(jsonObject: JsonObject): void {\n return JsonFile._validateNoUndefinedMembers(jsonObject, []);\n }\n\n // Private implementation of validateNoUndefinedMembers()\n private static _validateNoUndefinedMembers(jsonObject: JsonObject, keyPath: string[]): void {\n if (!jsonObject) {\n return;\n }\n if (typeof jsonObject === 'object') {\n for (const key of Object.keys(jsonObject)) {\n keyPath.push(key);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const value: any = jsonObject[key];\n if (value === undefined) {\n const fullPath: string = JsonFile._formatKeyPath(keyPath);\n throw new Error(`The value for ${fullPath} is \"undefined\" and cannot be serialized as JSON`);\n }\n\n JsonFile._validateNoUndefinedMembers(value, keyPath);\n keyPath.pop();\n }\n }\n }\n\n // Given this input: ['items', '4', 'syntax', 'parameters', 'string \"with\" symbols\", 'type']\n // Return this string: items[4].syntax.parameters[\"string \\\"with\\\" symbols\"].type\n private static _formatKeyPath(keyPath: string[]): string {\n let result: string = '';\n\n for (const key of keyPath) {\n if (/^[0-9]+$/.test(key)) {\n // It's an integer, so display like this: parent[123]\n result += `[${key}]`;\n } else if (/^[a-z_][a-z_0-9]*$/i.test(key)) {\n // It's an alphanumeric identifier, so display like this: parent.name\n if (result) {\n result += '.';\n }\n result += `${key}`;\n } else {\n // It's a freeform string, so display like this: parent[\"A path: \\\"C:\\\\file\\\"\"]\n\n // Convert this: A path: \"C:\\file\"\n // To this: A path: \\\"C:\\\\file\\\"\n const escapedKey: string = key\n .replace(/[\\\\]/g, '\\\\\\\\') // escape backslashes\n .replace(/[\"]/g, '\\\\'); // escape quotes\n result += `[\"${escapedKey}\"]`;\n }\n }\n return result;\n }\n\n private static _formatJsonHeaderComment(headerComment: string): string {\n if (headerComment === '') {\n return '';\n }\n const lines: string[] = headerComment.split('\\n');\n const result: string[] = [];\n for (const line of lines) {\n if (!/^\\s*$/.test(line) && !/^\\s*\\/\\//.test(line)) {\n throw new Error(\n 'The headerComment lines must be blank or start with the \"//\" prefix.\\n' +\n 'Invalid line' +\n JSON.stringify(line)\n );\n }\n result.push(Text.replaceAll(line, '\\r', ''));\n }\n return lines.join('\\n') + '\\n';\n }\n\n private static _buildJjuParseOptions(options: IJsonFileParseOptions | undefined): jju.ParseOptions {\n if (!options) {\n options = {};\n }\n const parseOptions: jju.ParseOptions = {};\n switch (options.jsonSyntax) {\n case JsonSyntax.Strict:\n parseOptions.mode = 'json';\n break;\n case JsonSyntax.JsonWithComments:\n parseOptions.mode = 'cjson';\n break;\n case JsonSyntax.Json5:\n default:\n parseOptions.mode = 'json5';\n break;\n }\n\n return parseOptions;\n }\n}\n"]} |