Something went wrong. Try again.
Monorepo for PackTools - tooling for Minecraft Modpacks!
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135--!pw caps=-- @pw/json: JSON written as Packwand writes its own (`canonical_json`): objects keep the order-- their members were set in, `pretty` indents by two spaces with `": "` and ends with a newline,-- `compact` has no spaces at all. Strings escape only `"`, `\` and control characters (`\u` in-- lowercase hex); everything else, `/` and DEL included, stays raw. Numbers are whole: a port-- that needs a fraction writes it some other way. A Luau table has no member order, so objects-- and arrays are built with `json.object` and `json.array`; any other table is refused.
local json = {}
local OBJECT = {}local ARRAY = {}
export type Object = { kind: any, keys: { string }, values: { [string]: any } }export type Array = { kind: any, items: { any } }
--- JSON's null, which a Luau table cannot hold as a value.json.null = table.freeze({})
--- An empty object; `json.set` adds its members in order.function json.object(): Object return { kind = OBJECT, keys = {}, values = {} }end
--- An array of `items` (copied), in order.function json.array(items: { any }?): Array local copy = {} for _, item in ipairs(items or {}) do table.insert(copy, item) end return { kind = ARRAY, items = copy }end
--- Sets `key` to `value`: a new key goes last, an existing one keeps its place.function json.set(object: Object, key: string, value: any) assert(object.kind == OBJECT, "json.set needs a json.object") if object.values[key] == nil then table.insert(object.keys, key) end object.values[key] = valueend
--- Appends `value` to `array`.function json.push(array: Array, value: any) assert(array.kind == ARRAY, "json.push needs a json.array") table.insert(array.items, value)end
local escapes = { ['"'] = '\\"', ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t",}
local function escape(byte: string): string return escapes[byte] or string.format("\\u%04x", string.byte(byte))end
local function quote(text: string): string return '"' .. string.gsub(text, '[%z\1-\31"\\]', escape) .. '"'end
local function scalar(value: any): string local kind = type(value) if kind == "string" then return quote(value) elseif kind == "boolean" then return if value then "true" else "false" elseif value == json.null then return "null" elseif kind == "number" then assert(value == math.floor(value) and math.abs(value) < 2 ^ 53, "JSON numbers here are whole") return if value == 0 then "0" else string.format("%.0f", value) end error(`a {kind} is not JSON; build objects and arrays with json.object and json.array`)end
local function write(out: { string }, value: any, depth: number?) local kind = if type(value) == "table" then value.kind else nil local inner = if depth then string.rep(" ", depth + 1) else "" local between = if depth then ",\n" else "," if kind == OBJECT then if #value.keys == 0 then table.insert(out, "{}") return end table.insert(out, if depth then "{\n" else "{") for i, key in ipairs(value.keys) do if i > 1 then table.insert(out, between) end table.insert(out, inner .. quote(key) .. (if depth then ": " else ":")) write(out, value.values[key], if depth then depth + 1 else nil) end table.insert(out, if depth then "\n" .. string.rep(" ", depth) .. "}" else "}") elseif kind == ARRAY then if #value.items == 0 then table.insert(out, "[]") return end table.insert(out, if depth then "[\n" else "[") for i, item in ipairs(value.items) do if i > 1 then table.insert(out, between) end table.insert(out, inner) write(out, item, if depth then depth + 1 else nil) end table.insert(out, if depth then "\n" .. string.rep(" ", depth) .. "]" else "]") else table.insert(out, scalar(value)) endend
--- `value` as a pack file holds it: indented, with a final newline.function json.pretty(value: any): string local out = {} write(out, value, 0) table.insert(out, "\n") return table.concat(out)end
--- `value` on one line, without spaces or a final newline.function json.compact(value: any): string local out = {} write(out, value, nil) return table.concat(out)end
return json