| 1 | /*
|
|---|
| 2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
|---|
| 3 | Author Natsu @xiaoxiaojx
|
|---|
| 4 | */
|
|---|
| 5 |
|
|---|
| 6 | "use strict";
|
|---|
| 7 |
|
|---|
| 8 | const stripJsonComments = require("./strip-json-comments");
|
|---|
| 9 |
|
|---|
| 10 | /** @typedef {import("../Resolver").FileSystem} FileSystem */
|
|---|
| 11 | /** @typedef {import("../Resolver").JsonObject} JsonObject */
|
|---|
| 12 |
|
|---|
| 13 | /**
|
|---|
| 14 | * @typedef {object} ReadJsonOptions
|
|---|
| 15 | * @property {boolean=} stripComments Whether to strip JSONC comments
|
|---|
| 16 | */
|
|---|
| 17 |
|
|---|
| 18 | /** @type {WeakMap<Buffer, JsonObject>} */
|
|---|
| 19 | const _stripCommentsCache = new WeakMap();
|
|---|
| 20 |
|
|---|
| 21 | /**
|
|---|
| 22 | * Read and parse JSON file (supports JSONC with comments).
|
|---|
| 23 | * Callback-based so a synchronous `fileSystem` stays synchronous all the
|
|---|
| 24 | * way through — Promise wrapping would defer resolution by a Promise tick
|
|---|
| 25 | * and break `resolveSync` when `tsconfig` is used together with
|
|---|
| 26 | * `useSyncFileSystemCalls: true`.
|
|---|
| 27 | * @param {FileSystem} fileSystem the file system
|
|---|
| 28 | * @param {string} jsonFilePath absolute path to JSON file
|
|---|
| 29 | * @param {ReadJsonOptions} options Options
|
|---|
| 30 | * @param {(err: NodeJS.ErrnoException | Error | null, content?: JsonObject) => void} callback callback
|
|---|
| 31 | * @returns {void}
|
|---|
| 32 | */
|
|---|
| 33 | function readJson(fileSystem, jsonFilePath, options, callback) {
|
|---|
| 34 | const { stripComments = false } = options;
|
|---|
| 35 | const { readJson: fsReadJson } = fileSystem;
|
|---|
| 36 | if (fsReadJson && !stripComments) {
|
|---|
| 37 | fsReadJson(jsonFilePath, (err, content) => {
|
|---|
| 38 | if (err) return callback(err);
|
|---|
| 39 | callback(null, /** @type {JsonObject} */ (content));
|
|---|
| 40 | });
|
|---|
| 41 | return;
|
|---|
| 42 | }
|
|---|
| 43 |
|
|---|
| 44 | fileSystem.readFile(jsonFilePath, (err, data) => {
|
|---|
| 45 | if (err) return callback(err);
|
|---|
| 46 | const buf = /** @type {Buffer} */ (data);
|
|---|
| 47 |
|
|---|
| 48 | if (stripComments) {
|
|---|
| 49 | const cached = _stripCommentsCache.get(buf);
|
|---|
| 50 | if (cached !== undefined) return callback(null, cached);
|
|---|
| 51 | }
|
|---|
| 52 |
|
|---|
| 53 | let result;
|
|---|
| 54 | try {
|
|---|
| 55 | const jsonText = buf.toString();
|
|---|
| 56 | const jsonWithoutComments = stripComments
|
|---|
| 57 | ? stripJsonComments(jsonText, {
|
|---|
| 58 | trailingCommas: true,
|
|---|
| 59 | whitespace: true,
|
|---|
| 60 | })
|
|---|
| 61 | : jsonText;
|
|---|
| 62 | result = JSON.parse(jsonWithoutComments);
|
|---|
| 63 | } catch (parseErr) {
|
|---|
| 64 | return callback(/** @type {Error} */ (parseErr));
|
|---|
| 65 | }
|
|---|
| 66 |
|
|---|
| 67 | if (stripComments) {
|
|---|
| 68 | _stripCommentsCache.set(buf, result);
|
|---|
| 69 | }
|
|---|
| 70 |
|
|---|
| 71 | callback(null, result);
|
|---|
| 72 | });
|
|---|
| 73 | }
|
|---|
| 74 |
|
|---|
| 75 | module.exports.readJson = readJson;
|
|---|