diff --git a/.gitignore b/.gitignore index 3b4f5af..05c4f98 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules grammar.js -package-lock.json \ No newline at end of file +package-lock.json +.pnpm-debug.log \ No newline at end of file diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..c6ae478 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,47 @@ +declare module "mojangson" { + type MojangsonNumericType = 'byte' | 'short' | 'long' | 'float' | 'double' | 'int'; + type MojangsonArrayElementType = 'byte' | 'int' | 'long'; + type MojangsonElementaryType = 'string' | 'boolean' | MojangsonNumericType; + type MojangsonArrayType = 'byteArray' | 'intArray' | 'longArray'; + type MojangsonAnyType = 'compound' | MojangsonElementaryType | MojangsonArrayType; + type MojangsonAnyValue = MojangsonEntry | string | boolean | number; + + interface MojangsonNumber { + value: number; + type: 'byte' | 'short' | 'long' | 'float' | 'double' | 'int'; + }; + interface MojangsonBoolean { + value: boolean; + type: 'boolean'; + }; + interface MojangsonArray { + value: { + value: number[]; + type: 'byte' | 'int' | 'long'; + }; + type: 'byteArray' | 'intArray' | 'longArray'; + }; + interface MojangsonList { + value: { + type: MojangsonAnyType; + value: MojangsonAnyValue[]; + }; + type: 'list'; + }; + interface MojangsonString { + value: string; + type: 'string'; + }; + interface MojangsonCompound { + value: { + [key: string]: MojangsonEntry; + }; + type: 'compound'; + } + type MojangsonEntry = MojangsonArray | MojangsonList | MojangsonString | MojangsonNumber | MojangsonBoolean | MojangsonCompound; + + function simplify(data: MojangsonEntry): any; + function stringify({ value, type }: MojangsonEntry, quotes?: boolean): string; + function parse(text: string): MojangsonEntry; + function normalize(str: string, quotes?: boolean): string; +} diff --git a/index.js b/index.js index 6763422..7f2f8e7 100644 --- a/index.js +++ b/index.js @@ -17,45 +17,46 @@ function simplify (data) { return transform(data.value, data.type) } -function stringify ({ value, type }) { +function stringify ({ value, type }, quotes = false) { if (type === 'compound') { const str = [] const entries = Object.entries(value) for (let i = 0; i < entries.length; i++) { const _type = entries[i][0] - let _value = stringify(entries[i][1]) - if (_type === 'string') _value = normalizeString(_value) - str.push(`${_type}:${_value}`) + let _value = stringify(entries[i][1], quotes) + if (_type === 'string') _value = normalizeString(_value, quotes) + if (quotes) { str.push(`"${_type}":${_value}`) } else { str.push(`${_type}:${_value}`) } } return `{${str.join(',')}}` } else if (type === 'list') { if (!Array.isArray(value.value)) return '[]' - const arrayElements = getArrayValues(value) + const arrayElements = getArrayValues(value, quotes) return `[${arrayElements}]` } else if (type === 'byteArray' || type === 'intArray' || type === 'longArray') { const prefix = getArrayPrefix(type) - const arrayElements = getArrayValues(value) + const arrayElements = getArrayValues(value, quotes) return `[${prefix}${arrayElements}]` } let str = value + getSuffix(value, type) - if (type === 'string') str = normalizeString(str) + if (type === 'string') str = normalizeString(str, quotes) return str } -function normalizeString (str) { +function normalizeString (str, quotes) { str = str.replace(/"/g, '\\"') if (/'|{|}|\[|\]|:|;|,|\(|\)|§|=/g.test(str) || str === '') str = `"${str}"` + if (quotes && !str.startsWith('"')) { str = `"${str}"` } return str } -function getArrayValues ({ value: arr, type }) { +function getArrayValues ({ value: arr, type }, quotes) { const hasMissingEl = hasMissingElements(arr) const str = [] // add nullable length that way [] is pased as [] for (let i = 0; i < arr.length; i++) { let curr = arr[i] if (curr !== undefined) { - curr = stringify({ value: curr, type }) + curr = stringify({ value: curr, type }, quotes) if (hasMissingEl) str.push(`${i}:${curr}`) else str.push(curr) } @@ -83,8 +84,8 @@ function getSuffix (val, type) { * @returns {string} the normalized mojangson */ -function normalize (str) { - return stringify(parse(str)) +function normalize (str, quotes = false) { + return stringify(parse(str), quotes) } function parse (text) { diff --git a/package.json b/package.json index 30ec8ac..f2bbba8 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "mojangson", - "version": "2.0.2", + "version": "2.0.3", "description": "A mojangson parser written in node.js", "main": "index.js", + "types": "index.d.ts", "repository": { "type": "git", "url": "git://github.com/rom1504/node-mojangson.git" diff --git a/test/quotes.test.js b/test/quotes.test.js new file mode 100644 index 0000000..26c6b1c --- /dev/null +++ b/test/quotes.test.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ +const assert = require('assert') +const mojangson = require('../') + +describe('Always use quotes for names and strings', () => { + // Seems that from a certain MC version SNBTs stopped working without quotes (as for 1.18.2) + [ + '{"var":"value1","var1":123b,"var2":"value3","var3":"value4"}', + '[B;1b,2b,3b]', + '{"id":"minecraft:yellow_shulker_box","Count":1b,"tag":{"BlockEntityTag":{"CustomName":"Stacked Totems","x":0,"y":0,"z":0,"id":"minecraft:shulker_box","Lock":""},"display":{"Name":"Stacked Totems"}},"Damage":0s}', + '{"SomeField":"кириллиц А"}', + '{"AField":"\\""}' + ].forEach(s => { + it('sould remain unchanged', () => { + assert.strictEqual(mojangson.stringify(mojangson.parse(s), true), s) + }) + }); + // Formatting tests + [ + ['{var: value1, var1:123b, "var2":value3, "var3": "value4"}', '{"var":"value1","var1":123b,"var2":"value3","var3":"value4"}'], + ['{SomeField: кириллицА}', '{"SomeField":"кириллицА"}'], + ['{AField: "\\""}', '{"AField":"\\""}'] + ].forEach(([input, expected]) => { + it('sould be equal', () => { + assert.strictEqual(mojangson.stringify(mojangson.parse(input), true), expected) + }) + }) +})