diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 54879bb..6e09b44 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,23 +16,16 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - node-version: [18.x, 20.x, 22.x] + node-version: [24.x] + + runs-on: ${{ matrix.os }} - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm i - run: npm run build --if-present - run: npm test - - - name: Coveralls - uses: coverallsapp/github-action@main - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - path-to-lcov: ./coverage/lcov.info - diff --git a/.gitignore b/.gitignore index 2921d03..b88cd27 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ coverage package-lock.json yarn.lock + +dist +dist-test +*.tsbuildinfo diff --git a/ini.d.ts b/ini.d.ts deleted file mode 100644 index 3d45355..0000000 --- a/ini.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Jsonic } from 'jsonic'; -type IniOptions = {}; -declare function Ini(jsonic: Jsonic, _options: IniOptions): void; -export { Ini }; -export type { IniOptions }; diff --git a/ini.js b/ini.js deleted file mode 100644 index 730a8f2..0000000 --- a/ini.js +++ /dev/null @@ -1,271 +0,0 @@ -"use strict"; -/* Copyright (c) 2021-2025 Richard Rodger, MIT License */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Ini = Ini; -const hoover_1 = require("@jsonic/hoover"); -function Ini(jsonic, _options) { - jsonic.use(hoover_1.Hoover, { - lex: { - order: 8.5e6, - }, - block: { - endofline: { - start: { - rule: { - parent: { - include: ['pair', 'elem'], - }, - }, - }, - end: { - fixed: ['\n', '\r\n', '#', ';', ''], - consume: ['\n', '\r\n'], - }, - escapeChar: '\\', - escape: { - '#': '#', - ';': ';', - '\\': '\\', - }, - preserveEscapeChar: true, - trim: true, - }, - key: { - token: '#HK', - start: { - rule: { - current: { - exclude: ['dive'], - }, - state: 'oc', - }, - }, - end: { - fixed: ['=', '\n', '\r\n', '#', ';', ''], - consume: false, - }, - escape: { - '#': '#', - ';': ';', - '\\': '\\', - }, - trim: true, - }, - divekey: { - token: '#DK', - start: { - rule: { - current: { - include: ['dive'], - }, - }, - }, - end: { - fixed: [']', '.'], - consume: false, - }, - escapeChar: '\\', - escape: { - ']': ']', - '.': '.', - '\\': '\\', - }, - allowUnknownEscape: true, - trim: true, - }, - }, - }); - jsonic.options({ - rule: { - start: 'ini', - exclude: 'jsonic', - }, - lex: { - emptyResult: {}, - }, - fixed: { - token: { - '#EQ': '=', - '#DOT': '.', - '#OB': null, - '#CB': null, - '#CL': null, - }, - }, - line: { - check: (lex) => { - if ('val' === lex.ctx.rule.name) { - return { done: true, token: undefined }; - } - }, - }, - number: { - lex: false, - }, - string: { - lex: true, - chars: `'"`, - abandon: true, - }, - text: { - lex: false, - }, - comment: { - def: { - hash: { eatline: true }, - slash: null, - multi: null, - semi: { line: true, start: ';', lex: true, eatline: true }, - }, - }, - }); - const { ZZ, ST, VL, OS, CS, CL, EQ, DOT, HV, HK, DK } = jsonic.token; - const KEY = [HK, ST, VL]; - jsonic.rule('ini', (rs) => { - rs.bo((r) => { - r.node = {}; - }).open([ - { s: [OS], p: 'table', b: 1 }, - { s: [KEY, EQ], p: 'table', b: 2 }, - { s: [HV, OS], p: 'table', b: 2 }, - { s: [ZZ] }, - ]); - }); - jsonic.rule('table', (rs) => { - rs.bo((r) => { - r.node = r.parent.node; - if (r.prev.u.dive) { - let dive = r.prev.u.dive; - for (let dI = 0; dI < dive.length; dI++) { - r.node = r.node[dive[dI]] = r.node[dive[dI]] || {}; - } - } - }) - .open([ - { s: [OS], p: 'dive' }, - { s: [KEY, EQ], p: 'map', b: 2 }, - { s: [HV, OS], p: 'map', b: 2 }, - { s: [CS], p: 'map' }, - { s: [ZZ] }, - ]) - .bc((r) => { - Object.assign(r.node, r.child.node); - }) - .close([ - { s: [OS], r: 'table', b: 1 }, - { s: [CS], r: 'table', a: (r) => (r.u.dive = r.child.u.dive) }, - { s: [ZZ] }, - ]); - }); - // TODO: maybe backport this to toml? - jsonic.rule('dive', (rs) => { - rs.open([ - { - s: [DK, DOT], - a: (r) => (r.u.dive = r.parent.u.dive || []).push(r.o0.val), - p: 'dive', - }, - { - s: [DK], - a: (r) => (r.u.dive = r.parent.u.dive || []).push(r.o0.val), - }, - ]).close([{ s: [CS], b: 1 }]); - }); - jsonic.rule('map', (rs) => { - rs.open([ - // Pair from implicit map. - { - s: [KEY, EQ], - c: (r) => 'table' === r.parent.name, - p: 'pair', - b: 2, - }, - { - s: [KEY], - c: (r) => 'table' === r.parent.name, - p: 'pair', - b: 1, - }, - ], { append: true }).close([{ s: [OS], b: 1 }, { s: [ZZ] }]); - }); - jsonic.rule('pair', (rs) => { - rs.open([ - { - s: [KEY, EQ], - c: (r) => 'table' === r.parent.parent.name, - p: 'val', - a: (r) => { - let key = '' + r.o0.val; - if (Array.isArray(r.node[key])) { - r.u.ini_array = r.node[key]; - } - else { - r.u.key = key; - if (2 < key.length && key.endsWith('[]')) { - key = r.u.key = key.slice(0, -2); - r.node[key] = r.u.ini_array = Array.isArray(r.node[key]) - ? r.node[key] - : undefined === r.node[key] - ? [] - : [r.node[key]]; - } - else { - r.u.pair = true; - } - } - }, - }, - // Special case: key by itself means key=true - { - s: [HK], - c: (r) => 'table' === r.parent.parent.name, - a: (r) => { - let key = r.o0.val; - if ('string' === typeof key && 0 < key.length) { - r.parent.node[key] = true; - } - }, - }, - ]).close([ - { - s: [KEY, CL], - c: (r) => 'table' === r.parent.parent.name, - e: (r) => { - return r.c1; - }, - }, - { s: [KEY], b: 1, r: 'pair' }, - { s: [OS], b: 1 }, - ]); - }); - jsonic.rule('val', (rs) => { - rs.open([ - // Since OS,CS are fixed tokens, concat them with string value - // if they appear as first char in a RHS value. - { - s: [[OS, CS]], - r: 'val', - u: { ini_prev: true }, - }, - { s: [ZZ], a: (r) => (r.node = '') }, - ], { - custom: (alts) => alts.filter((alt) => alt.g.join() !== 'json,list'), - }).ac((r) => { - if (ST === r.o0.tin && "'" === r.o0.src[0]) { - try { - r.node = JSON.parse(r.node); - } - catch (e) { - // Invalid JSON, just accept val as given - } - } - if (null != r.prev.u.ini_prev) { - r.prev.node = r.node = r.prev.o0.src + r.node; - } - else if (r.parent.u.ini_array) { - r.parent.u.ini_array.push(r.node); - } - }); - }); -} -//# sourceMappingURL=ini.js.map \ No newline at end of file diff --git a/ini.js.map b/ini.js.map deleted file mode 100644 index a596da0..0000000 --- a/ini.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ini.js","sourceRoot":"","sources":["ini.ts"],"names":[],"mappings":";AAAA,yDAAyD;;AAuShD,kBAAG;AAnSZ,2CAAuC;AAMvC,SAAS,GAAG,CAAC,MAAc,EAAE,QAAoB;IAC/C,MAAM,CAAC,GAAG,CAAC,eAAM,EAAE;QACjB,GAAG,EAAE;YACH,KAAK,EAAE,KAAK;SACb;QACD,KAAK,EAAE;YACL,SAAS,EAAE;gBACT,KAAK,EAAE;oBACL,IAAI,EAAE;wBACJ,MAAM,EAAE;4BACN,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;yBAC1B;qBACF;iBACF;gBACD,GAAG,EAAE;oBACH,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC;oBACnC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC;iBACxB;gBACD,UAAU,EAAE,IAAI;gBAChB,MAAM,EAAE;oBACN,GAAG,EAAE,GAAG;oBACR,GAAG,EAAE,GAAG;oBACR,IAAI,EAAE,IAAI;iBACX;gBACD,kBAAkB,EAAE,IAAI;gBACxB,IAAI,EAAE,IAAI;aACX;YACD,GAAG,EAAE;gBACH,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE;oBACL,IAAI,EAAE;wBACJ,OAAO,EAAE;4BACP,OAAO,EAAE,CAAC,MAAM,CAAC;yBAClB;wBACD,KAAK,EAAE,IAAI;qBACZ;iBACF;gBACD,GAAG,EAAE;oBACH,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC;oBACxC,OAAO,EAAE,KAAK;iBACf;gBACD,MAAM,EAAE;oBACN,GAAG,EAAE,GAAG;oBACR,GAAG,EAAE,GAAG;oBACR,IAAI,EAAE,IAAI;iBACX;gBACD,IAAI,EAAE,IAAI;aACX;YACD,OAAO,EAAE;gBACP,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE;oBACL,IAAI,EAAE;wBACJ,OAAO,EAAE;4BACP,OAAO,EAAE,CAAC,MAAM,CAAC;yBAClB;qBACF;iBACF;gBACD,GAAG,EAAE;oBACH,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;oBACjB,OAAO,EAAE,KAAK;iBACf;gBACD,UAAU,EAAE,IAAI;gBAChB,MAAM,EAAE;oBACN,GAAG,EAAE,GAAG;oBACR,GAAG,EAAE,GAAG;oBACR,IAAI,EAAE,IAAI;iBACX;gBACD,kBAAkB,EAAE,IAAI;gBACxB,IAAI,EAAE,IAAI;aACX;SACF;KACF,CAAC,CAAA;IAEF,MAAM,CAAC,OAAO,CAAC;QACb,IAAI,EAAE;YACJ,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,QAAQ;SAClB;QACD,GAAG,EAAE;YACH,WAAW,EAAE,EAAE;SAChB;QACD,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,KAAK,EAAE,GAAG;gBACV,MAAM,EAAE,GAAG;gBACX,KAAK,EAAE,IAAI;gBACX,KAAK,EAAE,IAAI;gBACX,KAAK,EAAE,IAAI;aACZ;SACF;QAED,IAAI,EAAE;YACJ,KAAK,EAAE,CAAC,GAAQ,EAAE,EAAE;gBAClB,IAAI,KAAK,KAAK,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;oBAChC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;gBACzC,CAAC;YACH,CAAC;SACF;QACD,MAAM,EAAE;YACN,GAAG,EAAE,KAAK;SACX;QACD,MAAM,EAAE;YACN,GAAG,EAAE,IAAI;YACT,KAAK,EAAE,IAAI;YACX,OAAO,EAAE,IAAI;SACd;QACD,IAAI,EAAE;YACJ,GAAG,EAAE,KAAK;SACX;QAED,OAAO,EAAE;YACP,GAAG,EAAE;gBACH,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;gBACvB,KAAK,EAAE,IAAI;gBACX,KAAK,EAAE,IAAI;gBACX,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;aAC3D;SACF;KACF,CAAC,CAAA;IAEF,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,MAAM,CAAC,KAAK,CAAA;IAEpE,MAAM,GAAG,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;IAExB,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAY,EAAE,EAAE;QAClC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YACV,CAAC,CAAC,IAAI,GAAG,EAAE,CAAA;QACb,CAAC,CAAC,CAAC,IAAI,CAAC;YACN,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE;YAC7B,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE;YAClC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE;YACjC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;SACZ,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAY,EAAE,EAAE;QACpC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YACV,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAA;YAEtB,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAClB,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;gBACxB,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC;oBACxC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;gBACpD,CAAC;YACH,CAAC;QACH,CAAC,CAAC;aACC,IAAI,CAAC;YACJ,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE;YACtB,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE;YAChC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE;YAC/B,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE;YACrB,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;SACZ,CAAC;aACD,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YACR,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACrC,CAAC,CAAC;aACD,KAAK,CAAC;YACL,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE;YAC7B,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE;YAC9D,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;SACZ,CAAC,CAAA;IACN,CAAC,CAAC,CAAA;IAEF,qCAAqC;IACrC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAY,EAAE,EAAE;QACnC,EAAE,CAAC,IAAI,CAAC;YACN;gBACE,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC;gBACZ,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC;gBAC3D,CAAC,EAAE,MAAM;aACV;YACD;gBACE,CAAC,EAAE,CAAC,EAAE,CAAC;gBACP,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC;aAC5D;SACF,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAY,EAAE,EAAE;QAClC,EAAE,CAAC,IAAI,CACL;YACE,0BAA0B;YAC1B;gBACE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;gBACZ,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI;gBACnC,CAAC,EAAE,MAAM;gBACT,CAAC,EAAE,CAAC;aACL;YAED;gBACE,CAAC,EAAE,CAAC,GAAG,CAAC;gBACR,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI;gBACnC,CAAC,EAAE,MAAM;gBACT,CAAC,EAAE,CAAC;aACL;SACF,EACD,EAAE,MAAM,EAAE,IAAI,EAAE,CACjB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAY,EAAE,EAAE;QACnC,EAAE,CAAC,IAAI,CAAC;YACN;gBACE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;gBACZ,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI;gBAC1C,CAAC,EAAE,KAAK;gBACR,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;oBACP,IAAI,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAA;oBACvB,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;wBAC/B,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;oBAC7B,CAAC;yBAAM,CAAC;wBACN,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAA;wBACb,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;4BACzC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;4BAChC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gCACtD,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;gCACb,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;oCACzB,CAAC,CAAC,EAAE;oCACJ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;wBACrB,CAAC;6BAAM,CAAC;4BACN,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAA;wBACjB,CAAC;oBACH,CAAC;gBACH,CAAC;aACF;YAED,6CAA6C;YAC7C;gBACE,CAAC,EAAE,CAAC,EAAE,CAAC;gBACP,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI;gBAC1C,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;oBACP,IAAI,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAA;oBAClB,IAAI,QAAQ,KAAK,OAAO,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;wBAC9C,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA;oBAC3B,CAAC;gBACH,CAAC;aACF;SACF,CAAC,CAAC,KAAK,CAAC;YACP;gBACE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;gBACZ,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI;gBAC1C,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;oBACP,OAAO,CAAC,CAAC,EAAE,CAAA;gBACb,CAAC;aACF;YACD,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE;YAC7B,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;SAClB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAY,EAAE,EAAE;QAClC,EAAE,CAAC,IAAI,CACL;YACE,8DAA8D;YAC9D,+CAA+C;YAC/C;gBACE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBACb,CAAC,EAAE,KAAK;gBACR,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;aACtB;YAED,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,EAAE;SACrC,EACD;YACE,MAAM,EAAE,CAAC,IAAmB,EAAE,EAAE,CAC9B,IAAI,CAAC,MAAM,CAAC,CAAC,GAAgB,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,WAAW,CAAC;SAClE,CACF,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YACT,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3C,IAAI,CAAC;oBACH,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;gBAC7B,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,yCAAyC;gBAC3C,CAAC;YACH,CAAC;YAED,IAAI,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC9B,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAA;YAC/C,CAAC;iBAAM,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;gBAChC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;YACnC,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC"} \ No newline at end of file diff --git a/ini.min.js b/ini.min.js deleted file mode 100644 index 4b2c6ec..0000000 --- a/ini.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JsonicIni=e()}}((function(){var e={exports:{}};(function(t){(function(){!function(n){"object"==typeof e.exports?e.exports=n():("undefined"!=typeof window?window:void 0!==t?t:"undefined"!=typeof self?self:this).JsonicHoover=n()}((function(){var e={exports:{}};(function(t){(function(){!function(n){"object"==typeof e.exports?e.exports=n():("undefined"!=typeof window?window:void 0!==t?t:"undefined"!=typeof self?self:this).Jsonic=n()}((function(){var e=function(e){var t;return function(n){return t||e(t={exports:{},parent:n},t.exports),t.exports}},t=e((function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.values=t.keys=t.omap=t.isarr=t.entries=t.defprop=t.assign=t.S=t.JsonicError=void 0,t.badlex=function(e,t,n){let r=e.next.bind(e);return e.next=(e,l,i,o)=>{let s=r(e,l,i,o);if(t===s.tin){let t={};throw null!=s.use&&(t.use=s.use),new p(s.why||d.unexpected,t,s,e,n)}return s},e},t.charset=S,t.clean=I,t.clone=function(e){return g(Object.create(Object.getPrototypeOf(e)),e)},t.configure=function(e,t,n){var r,l,c,d,p,g,v,k,b,x,y,j,O,E,T,w,M,N,P,_,C,R,A,L,Y,J,F,K,U,B,$,V,Z,z,D,H,q,G,W,X,Q,ee,te,ne,re,le,ie,oe,se,ae;const ce=t||{};ce.t=ce.t||{},ce.tI=ce.tI||1;const ue=e=>f(e,ce);!1!==n.standard$&&(ue("#BD"),ue("#ZZ"),ue("#UK"),ue("#AA"),ue("#SP"),ue("#LN"),ue("#CM"),ue("#NR"),ue("#ST"),ue("#TX"),ue("#VL")),ce.safe={key:!1!==(null===(r=n.safe)||void 0===r?void 0:r.key)},ce.fixed={lex:!!(null===(l=n.fixed)||void 0===l?void 0:l.lex),token:n.fixed?u(I(n.fixed.token),([e,t])=>[t,f(e,ce)]):{},ref:void 0,check:null===(c=n.fixed)||void 0===c?void 0:c.check},ce.fixed.ref=u(ce.fixed.token,([e,t])=>[e,t]),ce.fixed.ref=Object.assign(ce.fixed.ref,u(ce.fixed.ref,([e,t])=>[t,e])),ce.match={lex:!!(null===(d=n.match)||void 0===d?void 0:d.lex),value:n.match?u(I(n.match.value),([e,t])=>[e,t]):{},token:n.match?u(I(n.match.token),([e,t])=>[f(e,ce),t]):{},check:null===(p=n.match)||void 0===p?void 0:p.check},u(ce.match.token,([e,t])=>[e,(t.tin$=+e,t)]);const de=n.tokenSet?Object.keys(n.tokenSet).reduce((e,t)=>(e[t]=n.tokenSet[t].filter(e=>null!=e).map(e=>ue(e)),e),{}):{};ce.tokenSet=ce.tokenSet||{},s(de).map(e=>{let t=e[0],n=e[1];ce.tokenSet[t]?(ce.tokenSet[t].length=0,ce.tokenSet[t].push(...n)):ce.tokenSet[t]=n}),ce.tokenSetTins=s(ce.tokenSet).reduce((e,t)=>(e[t[0]]=e[t[0]]||{},t[1].map(n=>e[t[0]][n]=!0),e),{}),ce.tokenSetTins.IGNORE=ce.tokenSetTins.IGNORE||{},ce.space={lex:!!(null===(g=n.space)||void 0===g?void 0:g.lex),chars:S(null===(v=n.space)||void 0===v?void 0:v.chars),check:null===(k=n.space)||void 0===k?void 0:k.check},ce.line={lex:!!(null===(b=n.line)||void 0===b?void 0:b.lex),chars:S(null===(x=n.line)||void 0===x?void 0:x.chars),rowChars:S(null===(y=n.line)||void 0===y?void 0:y.rowChars),single:!!(null===(j=n.line)||void 0===j?void 0:j.single),check:null===(O=n.line)||void 0===O?void 0:O.check},ce.text={lex:!!(null===(E=n.text)||void 0===E?void 0:E.lex),modify:((null===(T=ce.text)||void 0===T?void 0:T.modify)||[]).concat(((null===(w=n.text)||void 0===w?void 0:w.modify)?[n.text.modify]:[]).flat()).filter(e=>null!=e),check:null===(M=n.text)||void 0===M?void 0:M.check},ce.number={lex:!!(null===(N=n.number)||void 0===N?void 0:N.lex),hex:!!(null===(P=n.number)||void 0===P?void 0:P.hex),oct:!!(null===(_=n.number)||void 0===_?void 0:_.oct),bin:!!(null===(C=n.number)||void 0===C?void 0:C.bin),sep:null!=(null===(R=n.number)||void 0===R?void 0:R.sep)&&""!==n.number.sep,exclude:null===(A=n.number)||void 0===A?void 0:A.exclude,sepChar:null===(L=n.number)||void 0===L?void 0:L.sep,check:null===(Y=n.number)||void 0===Y?void 0:Y.check},ce.value={lex:!!(null===(J=n.value)||void 0===J?void 0:J.lex),def:s((null===(F=n.value)||void 0===F?void 0:F.def)||{}).reduce((e,t)=>(null==t[1]||!1===t[1]||t[1].match||(e[t[0]]=t[1]),e),{}),defre:s((null===(K=n.value)||void 0===K?void 0:K.def)||{}).reduce((e,t)=>(t[1]&&t[1].match&&(e[t[0]]=t[1],e[t[0]].consume=!!e[t[0]].consume),e),{})},ce.rule={start:null==(null===(U=n.rule)||void 0===U?void 0:U.start)?"val":n.rule.start,maxmul:null==(null===(B=n.rule)||void 0===B?void 0:B.maxmul)?3:n.rule.maxmul,finish:!!(null===($=n.rule)||void 0===$?void 0:$.finish),include:(null===(V=n.rule)||void 0===V?void 0:V.include)?n.rule.include.split(/\s*,+\s*/).filter(e=>""!==e):[],exclude:(null===(Z=n.rule)||void 0===Z?void 0:Z.exclude)?n.rule.exclude.split(/\s*,+\s*/).filter(e=>""!==e):[]},ce.map={extend:!!(null===(z=n.map)||void 0===z?void 0:z.extend),merge:null===(D=n.map)||void 0===D?void 0:D.merge},ce.list={property:!!(null===(H=n.list)||void 0===H?void 0:H.property)};let pe=Object.keys(ce.fixed.token).sort((e,t)=>t.length-e.length).map(e=>h(e)).join("|"),fe=(null===(q=n.comment)||void 0===q?void 0:q.lex)?(n.comment.def?o(n.comment.def):[]).filter(e=>e&&e.lex).map(e=>h(e.start)).join("|"):"",me=["([",h(i(S(ce.space.lex&&ce.space.chars,ce.line.lex&&ce.line.chars)).join("")),"]",("string"==typeof n.ender?n.ender.split(""):Array.isArray(n.ender)?n.ender:[]).map(e=>"|"+h(e)).join(""),""===pe?"":"|",pe,""===fe?"":"|",fe,"|$)"];return ce.rePart={fixed:pe,ender:me,commentStart:fe},ce.re={ender:m(null,...me),rowChars:m(null,h(null===(G=n.line)||void 0===G?void 0:G.rowChars)),columns:m(null,"["+h(null===(W=n.line)||void 0===W?void 0:W.chars)+"]","(.*)$")},ce.lex={empty:!!(null===(X=n.lex)||void 0===X?void 0:X.empty),emptyResult:null===(Q=n.lex)||void 0===Q?void 0:Q.emptyResult,match:(null===(ee=n.lex)||void 0===ee?void 0:ee.match)?s(n.lex.match).reduce((e,t)=>{let r=t[0],l=t[1];if(l){let t=l.make(ce,n);t&&(t.matcher=r,t.make=l.make,t.order=l.order),e.push(t)}return e},[]).filter(e=>null!=e&&!1!==e&&-1<+e.order).sort((e,t)=>e.order-t.order):[]},ce.parse={prepare:o(null===(te=n.parse)||void 0===te?void 0:te.prepare)},ce.debug={get_console:(null===(ne=n.debug)||void 0===ne?void 0:ne.get_console)||(()=>console),maxlen:null==(null===(re=n.debug)||void 0===re?void 0:re.maxlen)?99:n.debug.maxlen,print:{config:!!(null===(ie=null===(le=n.debug)||void 0===le?void 0:le.print)||void 0===ie?void 0:ie.config),src:null===(se=null===(oe=n.debug)||void 0===oe?void 0:oe.print)||void 0===se?void 0:se.src}},ce.error=n.error||{},ce.hint=n.hint||{},(null===(ae=n.config)||void 0===ae?void 0:ae.modify)&&i(n.config.modify).forEach(e=>n.config.modify[e](ce,n)),ce.debug.print.config&&ce.debug.get_console().dir(ce,{depth:null}),ce.result={fail:[]},n.result&&(ce.result.fail=[...n.result.fail]),a(e.options,n),a(e.token,ce.t),a(e.tokenSet,ce.tokenSet),a(e.fixed,ce.fixed.ref),ce},t.deep=g,t.errdesc=x,t.errinject=v,t.escre=h,t.errsite=k,t.filterRules=function(e,t){let n=["open","close"];for(let r of n)e.def[r]=e.def[r].map(e=>(e.g="string"==typeof e.g?(e.g||"").split(/\s*,+\s*/):e.g||[],e)).filter(e=>t.rule.include.reduce((t,n)=>t||null!=e.g&&-1!==e.g.indexOf(n),0===t.rule.include.length)).filter(e=>t.rule.exclude.reduce((t,n)=>t&&(null==e.g||-1===e.g.indexOf(n)),!0));return e},t.makelog=function(e,t){var n,r,l;let i=null===(l=null===(r=null===(n=e.opts)||void 0===n?void 0:n.plugin)||void 0===r?void 0:r.debug)||void 0===l?void 0:l.trace;if(t||i)if("number"==typeof(null==t?void 0:t.log)||i){let n=!1,r=null==t?void 0:t.log;(-1===r||i)&&(r=1,n=!0),e.log=(...t)=>{if(n){let n=t.filter(e=>d.object!=typeof e).map(e=>d.function==typeof e?e.name:e).join(d.gap);e.cfg.debug.get_console().log(n)}else e.cfg.debug.get_console().dir(t,{depth:r})}}else"function"==typeof t.log&&(e.log=t.log);return e.log},t.mesc=function(e,t){return(t=new String(e)).esc=!0,t},t.regexp=m,t.snip=O,t.srcfmt=y,t.tokenize=f,t.trimstk=function(e){e.stack&&(e.stack=e.stack.split("\n").filter(e=>!e.includes("jsonic/jsonic")).map(e=>e.replace(/ at /,"at ")).join("\n"))},t.parserwrap=function(e){return{start:function(t,n,i,o){try{return e.start(t,n,i,o)}catch(s){if("SyntaxError"===s.name){let o=0,a=0,c=0,u=r.EMPTY,d=s.message.match(/^Unexpected token (.) .*position\s+(\d+)/i);if(d){u=d[1],o=parseInt(d[2]),a=t.substring(0,o).replace(/[^\n]/g,r.EMPTY).length;let e=o-1;for(;-1t,root:()=>{},plgn:()=>n.internal().plugins,inst:()=>n,rule:{name:"no-rule"},sub:{},xs:-1,v2:m,v1:m,t0:m,t1:m,tC:-1,kI:-1,rs:[],rsI:0,rsm:{},n:{},log:i?i.log:void 0,F:y(n.internal().config),u:{},NORULE:{name:"no-rule"},NOTOKEN:{name:"no-token"}})}throw s}}}},t.prop=E,t.str=j,t.findTokenSet=function(e,t){return t.tokenSet[e]},t.modlist=function(e,t){if(t&&e){if(0null!=e);n.length!==e.length&&(e.length=0,e.push(...n))}if(t.custom){let n=t.custom(e);null!=n&&(e=n)}}return e},t.errmsg=b;const l=n({}),i=e=>null==e?[]:Object.keys(e);t.keys=i;const o=e=>null==e?[]:Object.values(e);t.values=o;const s=e=>null==e?[]:Object.entries(e);t.entries=s;const a=(e,...t)=>Object.assign(null==e?{}:e,...t);t.assign=a,t.isarr=e=>Array.isArray(e);const c=Object.defineProperty;t.defprop=c;const u=(e,t)=>Object.entries(e||{}).reduce((e,n)=>{let r=t?t(n):n;void 0===r[0]?delete e[n[0]]:e[r[0]]=r[1];let l=2;for(;void 0!==r[l];)e[r[l]]=r[l+1],l+=2;return e},{});t.omap=u;const d={indent:". ",logindent:" ",space:" ",gap:" ",Object:"Object",Array:"Array",object:"object",string:"string",function:"function",unexpected:"unexpected",map:"map",list:"list",elem:"elem",pair:"pair",val:"val",node:"node",no_re_flags:r.EMPTY,unprintable:"unprintable",invalid_ascii:"invalid_ascii",invalid_unicode:"invalid_unicode",invalid_lex_state:"invalid_lex_state",unterminated_string:"unterminated_string",unterminated_comment:"unterminated_comment",lex:"lex",parse:"parse",error:"error",none:"none",imp_map:"imp,map",imp_list:"imp,list",imp_null:"imp,null",end:"end",open:"open",close:"close",rule:"rule",stack:"stack",nUll:"null",name:"name",make:"make",colon:":"};t.S=d;class p extends SyntaxError{constructor(e,t,n,r,l){let i=x(e,t=g({},t),n,r,l);super(i.message),a(this,i)}}function f(e,t,n){let l=t.t,i=l[e];return null==i&&r.STRING===typeof e&&(i=t.tI++,l[i]=e,l[e]=i,l[e.substring(1)]=i,null!=n&&a(n.token,t.t)),i}function m(e,...t){return new RegExp(t.map(e=>e.esc?h(e.toString()):e).join(r.EMPTY),null==e?"":e)}function h(e){return null==e?"":e.replace(/[-\\|\]{}()[^$+*?.!=]/g,"\\$&").replace(/\t/g,"\\t").replace(/\r/g,"\\r").replace(/\n/g,"\\n")}function g(e,...t){let n=d.function===typeof e,r=null!=e&&(d.object===typeof e||n);for(let l of t){let t,i=d.function===typeof l,o=null!=l&&(d.object===typeof l||i);if(r&&o&&!i&&Array.isArray(e)===Array.isArray(l))for(let n in l)e[n]=g(e[n],l[n]);else e=void 0===l?e:i?l:o?d.function===typeof(t=l.constructor)&&d.Object!==t.name&&d.Array!==t.name?l:g(Array.isArray(l)?[]:{},l):l,n=d.function===typeof e,r=null!=e&&(d.object===typeof e||n)}return e}function v(e,t,n,r,l,i){return function(e,t,n){let r=typeof e,l=Array.isArray(e)?"array":null==e?"string":"object"===r?r:"string",i="object"===l?e:"array"===l?e.reduce((e,t,n)=>(e[n]=t,e),{}):{_:e},o=null==t?{}:t;return Object.entries(i).map(e=>i[e[0]]=null==e[1]?"":(""+e[1]).replace(/\{([\w_0-9.]+)}/g,(e,t)=>{var r;let l=E(o,t);if(l=void 0===l?e:l,"object"==typeof l){let e=null===(r=null==l?void 0:l.constructor)||void 0===r?void 0:r.name;l="Object"===e||"Array"===e?JSON.stringify(l).replace(/([^"])"/g,"$1"):l.toString()}else l=""+l;return n&&"string"==typeof n.indent&&(l=l.replace(/\n/g,"\n"+n.indent)),l})),"string"===l?i._:"array"===l?Object.values(i):i}(e,{...i||{},...i.cfg||{},...i.opts||{},...r||{},...l||{},...i.meta||{},...n||{},code:t,details:n,token:r,rule:l,ctx:i},{indent:" "})}function k(e){let{src:t,sub:n,msg:l,cline:i,row:o,col:s,pos:a}=e;o=null!=o&&0e+(n(null==i?"":i)+(r.EMPTY+f++).padStart(p," ")+" | "+(null==i?"":"\x1b[0m")+(null==e?r.EMPTY:e),h=u.length;return[2null!=e).join("\n")}function b(e){const t=null!=e.color&&"object"==typeof e.color?e.color:void 0,n=!0===e.color||t,r={reset:n?"\x1b[0m":"",hi:n?"\x1b[91m":"",lo:n?"\x1b[2m":"",line:n?"\x1b[34m":"",...t||{}};return[null==e.prefix?null:"function"==typeof e.prefix?e.prefix(r,e):""+e.prefix,(null==e.code?"":r.hi+"["+(null==e.name?"":e.name+"/")+e.code+"]:")+r.reset+" "+(null==e.msg?"":e.msg),null!=e.row&&null!=e.col||null!=e.file?" "+r.line+"--\x3e"+r.reset+" "+(null==e.file?"":e.file)+(null==e.row||null==e.col?"":":"+e.row+":"+e.col):null,null==e.src?"":k({src:e.src,sub:e.sub,msg:e.smsg||e.msg,cline:r.line,row:e.row,col:e.col,pos:e.pos})+"\n",null==e.hint?null:e.hint,null==e.suffix?null:"function"==typeof e.suffix?e.suffix(r,e):""+e.suffix].filter(e=>null!=e).join("\n")}function x(e,t,n,r,l){var i,o,s;try{let a=l.cfg,c=l.meta,u=v({msg:a.error[e]||(null===(i=null==t?void 0:t.use)||void 0===i?void 0:i.err)&&(t.use.err.code||t.use.err.message)||a.error.unknown,hint:(a.hint[e]||(null===(s=null===(o=t.use)||void 0===o?void 0:o.err)||void 0===s?void 0:s.message)||a.hint.unknown||"").trim().split("\n").map(e=>" "+e).join("\n")},e,t,n,r,l),d=b({code:e,name:"jsonic",msg:u.msg,hint:u.hint,src:l.src(),file:c?c.fileName:void 0,row:n.rI,col:n.cI,pos:n.sI,sub:n.src,color:!0,suffix:e=>[""," "+e.lo+"https://jsonic.senecajs.org"+e.reset," "+e.lo+"--internal: tag="+(l.opts.tag||"")+"; rule="+r.name+"~"+r.state+"; token="+f(n.tin,l.cfg)+(null==n.why?"":"~"+n.why)+"; plugins="+l.plgn().map(e=>e.name).join(",")+"--"+e.reset].join("\n")}),p={internal:{token:n,ctx:l}};return p={...Object.create(p),message:d,code:e,details:t,meta:c,fileName:c?c.fileName:void 0,lineNumber:n.rI,columnNumber:n.cI},p}catch(a){return console.log(a),{}}}function y(e){return"function"==typeof e.debug.print.src?e.debug.print.src:t=>{let n=null==t?r.EMPTY:Array.isArray(t)?JSON.stringify(t).replace(/]$/,s(t).filter(e=>isNaN(e[0])).map((e,t)=>(0===t?", ":"")+e[0]+": "+JSON.stringify(e[1]))+"]"):JSON.stringify(t);return n=n.substring(0,e.debug.maxlen)+(e.debug.maxlen!1!==e).map(e=>"object"==typeof e?i(e).join(r.EMPTY):e).join(r.EMPTY).split(r.EMPTY).reduce((e,t)=>(e[t]=t.charCodeAt(0),e),{})}function I(e){for(let t in e)null==e[t]&&delete e[t];return e}function E(e,t,n){let r=e;try{let r,l=t.split(".");for(let t=0;tnew i(...e);n.makePoint=o;class s{constructor(e,t,n,l,i,o,s){this.isToken=!0,this.name=r.EMPTY,this.tin=-1,this.val=void 0,this.src=r.EMPTY,this.sI=-1,this.rI=-1,this.cI=-1,this.len=-1,this.name=e,this.tin=t,this.src=l,this.val=n,this.sI=i.sI,this.rI=i.rI,this.cI=i.cI,this.use=o,this.why=s,this.len=null==l?0:l.length}resolveVal(e,t){return"function"==typeof this.val?this.val(e,t):this.val}bad(e,t){return this.err=e,null!=t&&(this.use=(0,l.deep)(this.use||{},t)),this}toString(){return"Token["+this.name+"="+this.tin+" "+(0,l.snip)(this.src)+(void 0===this.val||"#ST"===this.name||"#TX"===this.name?"":"="+(0,l.snip)(this.val))+" "+[this.sI,this.rI,this.cI]+(null==this.use?"":" "+(0,l.snip)(""+JSON.stringify(this.use).replace(/"/g,""),22))+(null==this.err?"":" "+this.err)+(null==this.why?"":" "+(0,l.snip)(""+this.why,22))+"]"}[r.INSPECT](){return this.toString()}}const a=(...e)=>new s(...e);function c(e,t,n){let r=e.pnt,l=t;if(e.cfg.fixed.lex&&null!=n&&0a("",-1,void 0,r.EMPTY,o(-1)),n.makeFixedMatcher=(e,t)=>{let n=(0,l.regexp)(null,"^(",e.rePart.fixed,")");return function(t){let r=e.fixed;if(!r.lex)return;if(e.fixed.check){let n=e.fixed.check(t);if(n&&n.done)return n.token}let l=t.pnt,i=t.src.substring(l.sI).match(n);if(i){let e=i[1],n=e.length;if(0{let n=(0,l.values)(e.match.value),r=(0,l.values)(e.match.token);return 0===n.length&&0===r.length?null:function(t,l,i=0){if(!e.match.lex)return;if(e.match.check){let n=e.match.check(t);if(n&&n.done)return n.token}let o=t.pnt,s=t.src.substring(o.sI),a="o"===l.state?0:1;for(let e of n)if(e.match instanceof RegExp){let n=s.match(e.match);if(n){let r=n[0],l=r.length;if(0{let n=t.comment;e.comment={lex:!!n&&!!n.lex,def:((null==n?void 0:n.def)?(0,l.entries)(n.def):[]).reduce((e,[t,n])=>{if(null==n||!1===n)return e;let r={name:t,start:n.start,end:n.end,line:!!n.line,lex:!!n.lex,eatline:!!n.eatline};return e[t]=r,e},{})};let r=e.comment.lex?(0,l.values)(e.comment.def).filter(e=>e.lex&&e.line):[],i=e.comment.lex?(0,l.values)(e.comment.def).filter(e=>e.lex&&!e.line):[];return function(t,n){if(!e.comment.lex)return;if(e.comment.check){let n=e.comment.check(t);if(n&&n.done)return n.token}let o=t.pnt,s=t.src.substring(o.sI),a=o.rI,c=o.cI;for(let l of r)if(s.startsWith(l.start)){let n=s.length,r=l.start.length;for(c+=l.start.length;r{let n=(0,l.regexp)(e.line.lex?null:"s","^(.*?)",...e.rePart.ender);return function(r){if(e.text.check){let t=e.text.check(r);if(t&&t.done)return t.token}let l=e.text,i=r.pnt,o=r.src.substring(i.sI),s=e.value.def,a=e.value.defre,u=o.match(n);if(u){let n,d=u[1],p=u[2];if(null!=d){let t=d.length;if(0{let n=e.number,r=(0,l.regexp)(null,["^([-+]?(0(",[n.hex?"x[0-9a-fA-F_]+":null,n.oct?"o[0-7_]+":null,n.bin?"b[01_]+":null].filter(e=>null!=e).join("|"),")|\\.?[0-9]+([0-9_]*[0-9])?)","(\\.[0-9]?([0-9_]*[0-9])?)?","([eE][-+]?[0-9]+([0-9_]*[0-9])?)?"].join("").replace(/_/g,n.sep?(0,l.escre)(n.sepChar):""),")",...e.rePart.ender),i=n.sep?(0,l.regexp)("g",(0,l.escre)(n.sepChar)):void 0;return function(t){if(n=e.number,!n.lex)return;if(e.number.check){let n=e.number.check(t);if(n&&n.done)return n.token}let l=t.pnt,o=t.src.substring(l.sI),s=e.value.def,a=o.match(r);if(a){let n,r=a[1],o=a[9],u=!0;if(null!=r&&(u=!e.number.exclude||!r.match(e.number.exclude))){let o=r.length;if(0{let n=t.string||{};return e.string=e.string||{},e.string=(0,l.deep)(e.string,{lex:!!(null==n?void 0:n.lex),quoteMap:(0,l.charset)(n.chars),multiChars:(0,l.charset)(n.multiChars),escMap:{...n.escape},escChar:n.escapeChar,escCharCode:null==n.escapeChar?void 0:n.escapeChar.charCodeAt(0),allowUnknown:!!n.allowUnknown,replaceCodeMap:(0,l.omap)((0,l.clean)({...n.replace}),([e,t])=>[e.charCodeAt(0),t]),hasReplace:!1,abandon:!!n.abandon}),e.string.escMap=(0,l.clean)(e.string.escMap),e.string.hasReplace=0<(0,l.keys)(e.string.replaceCodeMap).length,function(t){let n=e.string;if(!n.lex)return;if(e.string.check){let n=e.string.check(t);if(n&&n.done)return n.token}let{quoteMap:i,escMap:o,escChar:s,escCharCode:a,multiChars:c,allowUnknown:u,replaceCodeMap:d,hasReplace:p}=n,{pnt:f,src:m}=t,{sI:h,rI:g,cI:v}=f,k=m.length;if(i[m[h]]){const i=m[h],b=h,x=g,y=c[i];++h,++v;let j,O=[];for(;hfunction(t){if(!e.line.lex)return;if(e.line.check){let n=e.line.check(t);if(n&&n.done)return n.token}let n,{chars:r,rowChars:l}=e.line,{pnt:i,src:o}=t,{sI:s,rI:a}=i,c=e.line.single;for(c&&(n={});r[o[s]]&&!(n&&(n[o[s]]=(n[o[s]]||0)+1,c&&1function(t){if(!e.space.lex)return;if(e.space.check){let n=e.space.check(t);if(n&&n.done)return n.token}let{chars:n}=e.space,{pnt:r,src:l}=t,{sI:i,cI:o}=r;for(;n[l[i]];)i++,o++;if(r.sIt(i,e,this.ctx)),i}tokenize(e){return(0,l.tokenize)(e,this.cfg)}bad(e,t,n){return this.token("#BD",void 0,0<=t&&t<=n?this.src.substring(t,n):this.src[this.pnt.sI],void 0,void 0,e)}}n.makeLex=(...e)=>new u(...e)})),r={};Object.defineProperty(r,"__esModule",{value:!0}),r.STRING=r.INSPECT=r.EMPTY=r.AFTER=r.BEFORE=r.CLOSE=r.OPEN=void 0,r.OPEN="o",r.CLOSE="c",r.BEFORE="b",r.AFTER="a",r.EMPTY="",r.INSPECT=Symbol.for("nodejs.util.inspect.custom"),r.STRING="string";var l={};Object.defineProperty(l,"__esModule",{value:!0}),l.defaults=void 0;const i=n({}),o={safe:{key:!0},tag:"-",fixed:{lex:!0,token:{"#OB":"{","#CB":"}","#OS":"[","#CS":"]","#CL":":","#CA":","}},match:{lex:!0,token:{}},tokenSet:{IGNORE:["#SP","#LN","#CM"],VAL:["#TX","#NR","#ST","#VL"],KEY:["#TX","#NR","#ST","#VL"]},space:{lex:!0,chars:" \t"},line:{lex:!0,chars:"\r\n",rowChars:"\n",single:!1},text:{lex:!0},number:{lex:!0,hex:!0,oct:!0,bin:!0,sep:"_",exclude:void 0},comment:{lex:!0,def:{hash:{line:!0,start:"#",lex:!0,eatline:!1},slash:{line:!0,start:"//",lex:!0,eatline:!1},multi:{line:!1,start:"/*",end:"*/",lex:!0,eatline:!1}}},string:{lex:!0,chars:"'\"`",multiChars:"`",escapeChar:"\\",escape:{b:"\b",f:"\f",n:"\n",r:"\r",t:"\t",v:"\v",'"':'"',"'":"'","`":"`","\\":"\\","/":"/"},allowUnknown:!0,abandon:!1},map:{extend:!0,merge:void 0},list:{property:!0},value:{lex:!0,def:{true:{val:!0},false:{val:!1},null:{val:null}}},ender:[],plugin:{},debug:{get_console:()=>console,maxlen:99,print:{config:!1,src:void 0}},error:{unknown:"unknown error: {code}",unexpected:"unexpected character(s): {src}",invalid_unicode:"invalid unicode escape: {src}",invalid_ascii:"invalid ascii escape: {src}",unprintable:"unprintable character: {src}",unterminated_string:"unterminated string: {src}",unterminated_comment:"unterminated comment: {src}",unknown_rule:"unknown rule: {rulename}",end_of_source:"unexpected end of source"},hint:{unknown:"\nSince the error is unknown, this is probably a bug inside jsonic\nitself, or a plugin. Please consider posting a github issue - thanks!\n\nCode: {code}, Details: \n{details}",unexpected:"\nThe character(s) {src} were not expected at this point as they do not\nmatch the expected syntax, even under the relaxed jsonic rules. If it\nis not obviously wrong, the actual syntax error may be elsewhere. Try\ncommenting out larger areas around this point until you get no errors,\nthen remove the comments in small sections until you find the\noffending syntax. NOTE: Also check if any plugins you are using\nexpect different syntax in this case.",invalid_unicode:"\nThe escape sequence {src} does not encode a valid unicode code point\nnumber. You may need to validate your string data manually using test\ncode to see how JavaScript will interpret it. Also consider that your\ndata may have become corrupted, or the escape sequence has not been\ngenerated correctly.",invalid_ascii:"\nThe escape sequence {src} does not encode a valid ASCII character. You\nmay need to validate your string data manually using test code to see\nhow JavaScript will interpret it. Also consider that your data may\nhave become corrupted, or the escape sequence has not been generated\ncorrectly.",unprintable:"\nString values cannot contain unprintable characters (character codes\nbelow 32). The character {src} is unprintable. You may need to remove\nthese characters from your source data. Also check that it has not\nbecome corrupted.",unterminated_string:"\nThis string has no end quote.",unterminated_comment:"\nThis comment is never closed.",unknown_rule:"\nNo rule named $rulename is defined. This is probably an error in the\ngrammar of a plugin.",end_of_source:"\nUnexpected end of source."},lex:{match:{match:{order:1e6,make:i.makeMatchMatcher},fixed:{order:2e6,make:i.makeFixedMatcher},space:{order:3e6,make:i.makeSpaceMatcher},line:{order:4e6,make:i.makeLineMatcher},string:{order:5e6,make:i.makeStringMatcher},comment:{order:6e6,make:i.makeCommentMatcher},number:{order:7e6,make:i.makeNumberMatcher},text:{order:8e6,make:i.makeTextMatcher}},empty:!0,emptyResult:void 0},parse:{prepare:{}},rule:{start:"val",finish:!0,maxmul:3,include:"",exclude:""},result:{fail:[]},config:{modify:{}},parser:{start:void 0}};l.defaults=o;var s={};Object.defineProperty(s,"__esModule",{value:!0}),s.makeRuleSpec=s.makeNoRule=s.makeRule=void 0;const a=t({});class c{constructor(e,t,n){this.i=-1,this.name=r.EMPTY,this.node=null,this.state=r.OPEN,this.n=Object.create(null),this.d=-1,this.u=Object.create(null),this.k=Object.create(null),this.bo=!1,this.ao=!1,this.bc=!1,this.ac=!1,this.os=0,this.cs=0,this.need=0,this.i=t.uI++,this.name=e.name,this.spec=e,this.child=t.NORULE,this.parent=t.NORULE,this.prev=t.NORULE,this.o0=t.NOTOKEN,this.o1=t.NOTOKEN,this.c0=t.NOTOKEN,this.c1=t.NOTOKEN,this.node=n,this.d=t.rsI,this.bo=null!=e.def.bo,this.ao=null!=e.def.ao,this.bc=null!=e.def.bc,this.ac=null!=e.def.ac}process(e,t){return this.spec.process(this,e,t,this.state)}eq(e,t=0){let n=this.n[e];return null==n||n===t}lt(e,t=0){let n=this.n[e];return null==n||nt}lte(e,t=0){let n=this.n[e];return null==n||n<=t}gte(e,t=0){let n=this.n[e];return null==n||n>=t}toString(){return"[Rule "+this.name+"~"+this.i+"]"}}const u=(...e)=>new c(...e);s.makeRule=u,s.makeNoRule=e=>u(g(e.cfg,{}),e);class d{constructor(){this.p=r.EMPTY,this.r=r.EMPTY,this.b=0}}const p=(...e)=>new d(...e),f=p(),m=p();class h{constructor(e,t){this.name=r.EMPTY,this.def={open:[],close:[],bo:[],bc:[],ao:[],ac:[],tcol:[]},this.cfg=e,this.def=Object.assign(this.def,t),this.def.open=(this.def.open||[]).filter(e=>null!=e),this.def.close=(this.def.close||[]).filter(e=>null!=e);for(let n of[...this.def.open,...this.def.close])v(n)}tin(e){return(0,a.tokenize)(e,this.cfg)}add(e,t,n){let r=(null==n?void 0:n.append)?"push":"unshift",l=((0,a.isarr)(t)?t:[t]).filter(e=>null!=e&&"object"==typeof e).map(e=>v(e)),i="o"===e?"open":"close",o=this.def[i];return o[r](...l),o=this.def[i]=(0,a.modlist)(o,n),(0,a.filterRules)(this,this.cfg),this.norm(),this}open(e,t){return this.add("o",e,t)}close(e,t){return this.add("c",e,t)}action(e,t,n,r){let l=this.def[t+n];return e?l.push(r):l.unshift(r),this}bo(e,t){return this.action(!t||!!e,r.BEFORE,r.OPEN,t||e)}ao(e,t){return this.action(!t||!!e,r.AFTER,r.OPEN,t||e)}bc(e,t){return this.action(!t||!!e,r.BEFORE,r.CLOSE,t||e)}ac(e,t){return this.action(!t||!!e,r.AFTER,r.CLOSE,t||e)}clear(){return this.def.open.length=0,this.def.close.length=0,this.def.bo.length=0,this.def.ao.length=0,this.def.bc.length=0,this.def.ac.length=0,this}norm(){this.def.open.map(e=>v(e)),this.def.close.map(e=>v(e));const e=[];function t(e,t,n){return n[e]=n[e]||[],[function(e,n){if(n.s&&n.s[t]){let r=[...new Set(e.concat(n.s[t]))];e.length=0,e.push(...r)}return e},n[e][t]=n[e][t]||[]]}return this.def.open.reduce(...t(0,0,e)),this.def.open.reduce(...t(0,1,e)),this.def.close.reduce(...t(1,0,e)),this.def.close.reduce(...t(1,1,e)),this.def.tcol=e,this}process(e,t,n,l){t.log&&t.log(a.S.rule,t,e,n);let i="o"===l,o=i?e:t.NORULE,s=i?"O":"C",c=this.def,d=i?c.open:c.close,p=i?e.bo?c.bo:null:e.bc?c.bc:null;if(p){let n;for(let r=0;rnew h(...e);function v(e){if(r.STRING===typeof e.g?e.g=e.g.split(/\s*,\s*/):null==e.g&&(e.g=[]),e.g=e.g.sort(),e.s&&0!==e.s.length){const t=e=>e.flat().filter(e=>"number"==typeof e),n=(e,t)=>e.filter(e=>31*t<=e&&e<31*(t+1)),r=(e,t)=>e.reduce((e,n)=>1<1+e/31|0))).fill(null).map((e,t)=>t).map(e=>r(n(l,e),e)):null,o.S1=01+e/31|0))).fill(null).map((e,t)=>t).map(e=>r(n(i,e),e)):null}else e.s=null;return e.p||(e.p=null),e.r||(e.r=null),e.b||(e.b=null),e}s.makeRuleSpec=g;var k={};Object.defineProperty(k,"__esModule",{value:!0}),k.makeParser=k.makeRuleSpec=k.makeRule=void 0;const b=t({}),x=n({});Object.defineProperty(k,"makeRule",{enumerable:!0,get:function(){return s.makeRule}}),Object.defineProperty(k,"makeRuleSpec",{enumerable:!0,get:function(){return s.makeRuleSpec}});class y{constructor(e,t){this.rsm={},this.options=e,this.cfg=t}rule(e,t){if(null==e)return this.rsm;let n=this.rsm[e];if(null===t)delete this.rsm[e];else if(void 0!==t)return n=this.rsm[e]=this.rsm[e]||(0,s.makeRuleSpec)(this.cfg,{}),n=this.rsm[e]=t(this.rsm[e],this)||this.rsm[e],void(n.name=e);return n}start(e,t,n,l){let i,o=(0,x.makeToken)("#ZZ",(0,b.tokenize)("#ZZ",this.cfg),void 0,r.EMPTY,(0,x.makePoint)(-1)),a=(0,x.makeNoToken)(),c={uI:0,opts:this.options,cfg:this.cfg,meta:n||{},src:()=>e,root:()=>i,plgn:()=>t.internal().plugins,inst:()=>t,rule:{},sub:t.internal().sub,xs:-1,v2:o,v1:o,t0:a,t1:a,tC:-2,kI:-1,rs:[],rsI:0,rsm:this.rsm,log:void 0,F:(0,b.srcfmt)(this.cfg),u:{},NOTOKEN:a,NORULE:{}};c=(0,b.deep)(c,l);let u=(0,s.makeNoRule)(c);if(c.NORULE=u,c.rule=u,n&&b.S.function===typeof n.log&&(c.log=n.log),this.cfg.parse.prepare.forEach(e=>e(t,c,n)),""===e){if(this.cfg.lex.empty)return this.cfg.lex.emptyResult;throw new b.JsonicError(b.S.unexpected,{src:e},c.t0,u,c)}let d=(0,b.badlex)((0,x.makeLex)(c),(0,b.tokenize)("#BD",this.cfg),c),p=this.rsm[this.cfg.rule.start];if(null==p)return;let f=(0,s.makeRule)(p,c);i=f;let m=2*(0,b.keys)(this.rsm).length*d.src.length*2*c.cfg.rule.maxmul,h=0;for(;u!==f&&he(f,c)),f=f.process(c,d),c.log&&c.log(b.S.stack,c,f,d),h++;if(o.tin!==d.next(f).tin)throw new b.JsonicError(b.S.unexpected,{},c.t0,u,c);const g=c.root().node;if(this.cfg.result.fail.includes(g))throw new b.JsonicError(b.S.unexpected,{},c.t0,u,c);return g}clone(e,t){let n=new y(e,t);return n.rsm=Object.keys(this.rsm).reduce((e,t)=>(e[t]=(0,b.filterRules)(this.rsm[t],this.cfg),e),{}),n.norm(),n}norm(){(0,b.values)(this.rsm).map(e=>e.norm())}}k.makeParser=(...e)=>new y(...e);var j={};function O(e){const{deep:t}=e.util,{OB:n,CB:r,OS:l,CS:i,CL:o,CA:s,TX:a,ST:c,ZZ:u}=e.token,{VAL:d,KEY:p}=e.tokenSet,f=(e,t)=>{if(!t.cfg.rule.finish)return t.t0.err="end_of_source",t.t0},m=e=>{const t=e.o0,n=c===t.tin||a===t.tin?t.val:t.src;e.u.key=n};e.rule("val",e=>{e.bo(e=>e.node=void 0).open([{s:[n],p:"map",b:1,g:"map,json"},{s:[l],p:"list",b:1,g:"list,json"},{s:[d],g:"val,json"}]).close([{s:[u],g:"end,json"},{b:1,g:"more,json"}]).bc((e,t)=>{e.node=void 0===e.node?void 0===e.child.node?0===e.os?void 0:e.o0.resolveVal(e,t):e.child.node:e.node})}),e.rule("map",e=>{e.bo(e=>{e.node=Object.create(null)}).open([{s:[n,r],b:1,n:{pk:0},g:"map,json"},{s:[n],p:"pair",n:{pk:0},g:"map,json,pair"}]).close([{s:[r],g:"end,json"}])}),e.rule("list",e=>{e.bo(e=>{e.node=[]}).open([{s:[l,i],b:1,g:"list,json"},{s:[l],p:"elem",g:"list,elem,json"}]).close([{s:[i],g:"end,json"}])}),e.rule("pair",e=>{e.open([{s:[p,o],p:"val",u:{pair:!0},a:m,g:"map,pair,key,json"}]).bc((e,t)=>{e.u.pair&&(e.u.prev=e.node[e.u.key],e.node[e.u.key]=e.child.node)}).close([{s:[s],r:"pair",g:"map,pair,json"},{s:[r],b:1,g:"map,pair,json"}])}),e.rule("elem",e=>{e.open([{p:"val",g:"list,elem,val,json"}]).bc(e=>{!0!==e.u.done&&e.node.push(e.child.node)}).close([{s:[s],r:"elem",g:"list,elem,json"},{s:[i],b:1,g:"list,elem,json"}])});const h=(e,n)=>{let r=e.u.key,l=e.child.node;const i=e.u.prev;l=void 0===l?null:l,e.u.list&&n.cfg.safe.key&&("__proto__"===r||"constructor"===r)||(e.node[r]=null==i?l:n.cfg.map.merge?n.cfg.map.merge(i,l,e,n):n.cfg.map.extend?t(i,l):l)};e.rule("val",e=>{e.open([{s:[p,o],p:"map",b:2,n:{pk:1},g:"pair,jsonic"},{s:[d],g:"val,json"},{s:[[r,i]],b:1,c:e=>00===e.d,p:"list",b:1,g:"list,imp,jsonic"},{s:[s],b:1,g:"list,val,imp,null,jsonic"},{s:[u],g:"jsonic"}],{append:!0,delete:[2]}).close([{s:[[r,i]],b:1,g:"val,json,close",e:(e,t)=>0===e.d?t.t0:void 0},{s:[s],c:e=>e.lte("dlist")&&e.lte("dmap"),r:"list",u:{implist:!0},g:"list,val,imp,comma,jsonic"},{c:e=>e.lte("dlist")&&e.lte("dmap"),r:"list",u:{implist:!0},g:"list,val,imp,space,jsonic",b:1},{s:[u],g:"jsonic"}],{append:!0,move:[1,-1]})}),e.rule("map",e=>{e.bo(e=>{e.n.dmap=1+(e.n.dmap?e.n.dmap:0)}).open([{s:[n,u],b:1,e:f,g:"end,jsonic"}]).open([{s:[p,o],p:"pair",b:2,g:"pair,list,val,imp,jsonic"}],{append:!0}).close([{s:[r],c:e=>e.lte("pk"),g:"end,json"},{s:[r],b:1,g:"path,jsonic"},{s:[[s,i,...d]],b:1,g:"end,path,jsonic"},{s:[u],e:f,g:"end,jsonic"}],{append:!0,delete:[0]})}),e.rule("list",e=>{e.bo(e=>{e.n.dlist=1+(e.n.dlist?e.n.dlist:0),e.prev.u.implist&&(e.node.push(e.prev.node),e.prev.node=e.node)}).open({c:e=>e.prev.u.implist,p:"elem"}).open([{s:[s],p:"elem",b:1,g:"list,elem,val,imp,jsonic"},{p:"elem",g:"list,elem.jsonic"}],{append:!0}).close([{s:[u],e:f,g:"end,jsonic"}],{append:!0})}),e.rule("pair",(e,t)=>{e.open([{s:[s],g:"map,pair,comma,jsonic"}],{append:!0}).bc((e,t)=>{e.u.pair&&h(e,t)}).close([{s:[r],c:e=>e.lte("pk"),b:1,g:"map,pair,json"},{s:[s,r],c:e=>e.lte("pk"),b:1,g:"map,pair,comma,jsonic"},{s:[s,u],g:"end,jsonic"},{s:[s],c:e=>e.lte("pk"),r:"pair",g:"map,pair,json"},{s:[s],c:e=>e.lte("dmap",1),r:"pair",g:"map,pair,jsonic"},{s:[p],c:e=>e.lte("dmap",1),r:"pair",b:1,g:"map,pair,imp,jsonic"},{s:[[r,s,i,...p]],c:e=>0e.c0,g:"end,jsonic"},{s:[u],e:f,g:"map,pair,json"},{r:"pair",b:1,g:"map,pair,imp,jsonic"}],{append:!0,delete:[0,1]})}),e.rule("elem",(e,t)=>{e.open([{s:[s,s],b:2,u:{done:!0},a:e=>e.node.push(null),g:"list,elem,imp,null,jsonic"},{s:[s],u:{done:!0},a:e=>e.node.push(null),g:"list,elem,imp,null,jsonic"},{s:[p,o],e:t.cfg.list.property?void 0:(e,t)=>t.t0,p:"val",n:{pk:1,dmap:1},u:{done:!0,pair:!0,list:!0},a:m,g:"elem,pair,jsonic"}]).bc((e,t)=>{!0===e.u.pair&&(e.u.prev=e.node[e.u.key],h(e,t))}).close([{s:[s,[i,u]],b:1,g:"list,elem,comma,jsonic"},{s:[s],r:"elem",g:"list,elem,json"},{s:[i],b:1,g:"list,elem,json"},{s:[u],e:f,g:"list,elem,json"},{s:[r],e:e=>e.c0,g:"end,jsonic"},{r:"elem",b:1,g:"list,elem,imp,jsonic"}],{delete:[-1,-2]})})}Object.defineProperty(j,"__esModule",{value:!0}),j.grammar=O,j.makeJSON=function(e){let t=e.make({grammar$:!1,text:{lex:!1},number:{hex:!1,oct:!1,bin:!1,sep:null,exclude:/^00+/},string:{chars:'"',multiChars:"",allowUnknown:!1,escape:{v:null}},comment:{lex:!1},map:{extend:!1},lex:{empty:!1},rule:{finish:!1,include:"json"},result:{fail:[void 0,NaN]},tokenSet:{KEY:["#ST",null,null,null]}});return O(t),t};var S={exports:{}};Object.defineProperty(S.exports,"__esModule",{value:!0}),S.exports.root=S.exports.S=S.exports.EMPTY=S.exports.AFTER=S.exports.BEFORE=S.exports.CLOSE=S.exports.OPEN=S.exports.makeTextMatcher=S.exports.makeNumberMatcher=S.exports.makeCommentMatcher=S.exports.makeStringMatcher=S.exports.makeLineMatcher=S.exports.makeSpaceMatcher=S.exports.makeFixedMatcher=S.exports.makeParser=S.exports.makeLex=S.exports.makeRuleSpec=S.exports.makeRule=S.exports.makePoint=S.exports.makeToken=S.exports.util=S.exports.JsonicError=S.exports.Jsonic=void 0,S.exports.make=w,Object.defineProperty(S.exports,"OPEN",{enumerable:!0,get:function(){return r.OPEN}}),Object.defineProperty(S.exports,"CLOSE",{enumerable:!0,get:function(){return r.CLOSE}}),Object.defineProperty(S.exports,"BEFORE",{enumerable:!0,get:function(){return r.BEFORE}}),Object.defineProperty(S.exports,"AFTER",{enumerable:!0,get:function(){return r.AFTER}}),Object.defineProperty(S.exports,"EMPTY",{enumerable:!0,get:function(){return r.EMPTY}});const I=t({});Object.defineProperty(S.exports,"JsonicError",{enumerable:!0,get:function(){return I.JsonicError}}),Object.defineProperty(S.exports,"S",{enumerable:!0,get:function(){return I.S}});const E=n({});Object.defineProperty(S.exports,"makePoint",{enumerable:!0,get:function(){return E.makePoint}}),Object.defineProperty(S.exports,"makeToken",{enumerable:!0,get:function(){return E.makeToken}}),Object.defineProperty(S.exports,"makeLex",{enumerable:!0,get:function(){return E.makeLex}}),Object.defineProperty(S.exports,"makeFixedMatcher",{enumerable:!0,get:function(){return E.makeFixedMatcher}}),Object.defineProperty(S.exports,"makeSpaceMatcher",{enumerable:!0,get:function(){return E.makeSpaceMatcher}}),Object.defineProperty(S.exports,"makeLineMatcher",{enumerable:!0,get:function(){return E.makeLineMatcher}}),Object.defineProperty(S.exports,"makeStringMatcher",{enumerable:!0,get:function(){return E.makeStringMatcher}}),Object.defineProperty(S.exports,"makeCommentMatcher",{enumerable:!0,get:function(){return E.makeCommentMatcher}}),Object.defineProperty(S.exports,"makeNumberMatcher",{enumerable:!0,get:function(){return E.makeNumberMatcher}}),Object.defineProperty(S.exports,"makeTextMatcher",{enumerable:!0,get:function(){return E.makeTextMatcher}}),Object.defineProperty(S.exports,"makeRule",{enumerable:!0,get:function(){return k.makeRule}}),Object.defineProperty(S.exports,"makeRuleSpec",{enumerable:!0,get:function(){return k.makeRuleSpec}}),Object.defineProperty(S.exports,"makeParser",{enumerable:!0,get:function(){return k.makeParser}});const T={tokenize:I.tokenize,srcfmt:I.srcfmt,clone:I.clone,charset:I.charset,trimstk:I.trimstk,makelog:I.makelog,badlex:I.badlex,errsite:I.errsite,errinject:I.errinject,errdesc:I.errdesc,configure:I.configure,parserwrap:I.parserwrap,mesc:I.mesc,escre:I.escre,regexp:I.regexp,prop:I.prop,str:I.str,clean:I.clean,errmsg:I.errmsg,deep:I.deep,omap:I.omap,keys:I.keys,values:I.values,entries:I.entries};function w(e,t){let n=!0;if("jsonic"===e)n=!1;else if("json"===e)return(0,j.makeJSON)(M);e="string"==typeof e?{}:e;let r={parser:null,config:null,plugins:[],sub:{lex:void 0,rule:void 0},mark:Math.random()},i=(0,I.deep)({},t?{...t.options}:!1===(null==e?void 0:e.defaults$)?{}:l.defaults,e||{}),o=function(e,t,n){var r;if(I.S.string===typeof e){let l=o.internal();return((null===(r=s.parser)||void 0===r?void 0:r.start)?(0,I.parserwrap)(s.parser):l.parser).start(e,o,t,n)}return e},s=e=>{if(null!=e&&I.S.object===typeof e){(0,I.deep)(i,e),(0,I.configure)(o,r.config,i);let t=o.internal().parser;r.parser=t.clone(i,r.config)}return{...o.options}},a={token:e=>(0,I.tokenize)(e,r.config,o),tokenSet:e=>(0,I.findTokenSet)(e,r.config),fixed:e=>r.config.fixed.ref[e],options:(0,I.deep)(s,i),config:()=>(0,I.deep)(r.config),parse:o,use:function(e,t){if(I.S.function!==typeof e)throw new Error("Jsonic.use: the first argument must be a function defining a plugin. See https://jsonic.senecajs.org/plugin");const n=e.name.toLowerCase(),r=(0,I.deep)({},e.defaults||{},t||{});o.options({plugin:{[n]:r}});let l=o.options.plugin[n];return o.internal().plugins.push(e),e.options=l,e(o,l)||o},rule:(e,t)=>o.internal().parser.rule(e,t)||o,make:e=>w(e,o),empty:e=>w({defaults$:!1,standard$:!1,grammar$:!1,...e||{}}),id:"Jsonic/"+Date.now()+"/"+(""+Math.random()).substring(2,8).padEnd(6,"0")+(null==s.tag?"":"/"+s.tag),toString:()=>a.id,sub:e=>(e.lex&&(r.sub.lex=r.sub.lex||[],r.sub.lex.push(e.lex)),e.rule&&(r.sub.rule=r.sub.rule||[],r.sub.rule.push(e.rule)),o),util:T};if((0,I.defprop)(a.make,I.S.name,{value:I.S.make}),n?(0,I.assign)(o,a):(0,I.assign)(o,{empty:a.empty,parse:a.parse,sub:a.sub,id:a.id,toString:a.toString}),(0,I.defprop)(o,"internal",{value:()=>r}),t){for(let n in t)void 0===o[n]&&(o[n]=t[n]);o.parent=t;let e=t.internal();r.config=(0,I.deep)({},e.config),(0,I.configure)(o,r.config,i),(0,I.assign)(o.token,r.config.t),r.plugins=[...e.plugins],r.parser=e.parser.clone(i,r.config)}else{let e={...o,...a};r.config=(0,I.configure)(e,void 0,i),r.plugins=[],r.parser=(0,k.makeParser)(i,r.config),!1!==i.grammar$&&(0,j.grammar)(e)}return o}let M;S.exports.util=T,S.exports.root=M;let N=S.exports.root=M=w("jsonic");return S.exports.Jsonic=N,M.Jsonic=M,M.JsonicError=I.JsonicError,M.makeLex=E.makeLex,M.makeParser=k.makeParser,M.makeToken=E.makeToken,M.makePoint=E.makePoint,M.makeRule=k.makeRule,M.makeRuleSpec=k.makeRuleSpec,M.makeFixedMatcher=E.makeFixedMatcher,M.makeSpaceMatcher=E.makeSpaceMatcher,M.makeLineMatcher=E.makeLineMatcher,M.makeStringMatcher=E.makeStringMatcher,M.makeCommentMatcher=E.makeCommentMatcher,M.makeNumberMatcher=E.makeNumberMatcher,M.makeTextMatcher=E.makeTextMatcher,M.OPEN=r.OPEN,M.CLOSE=r.CLOSE,M.BEFORE=r.BEFORE,M.AFTER=r.AFTER,M.EMPTY=r.EMPTY,M.util=T,M.make=w,M.S=I.S,S.exports.default=N,S.exports=N,S.exports}))}).call(this)}).call(this,void 0!==t?t:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{}),e=e.exports;var n={};Object.defineProperty(n,"__esModule",{value:!0}),n.Hoover=void 0,n.parseToEnd=i;const r=(t,n)=>{var r;const{entries:o}=t.util;let s=o(n.block).map(e=>({allowUnknownEscape:!0,preserveEscapeChar:!1,token:"#HV",...e[1],name:e[0]})),a={};for(let e of s)e.TOKEN=t.token(e.token),a[e.token]||t.rule("val",t=>{t.open({s:[e.TOKEN],a:n.action})}),a[e.token]=e.TOKEN;t.options({lex:{match:{hoover:{order:null===(r=n.lex)||void 0===r?void 0:r.order,make:(t,n)=>function(n){for(let r of s){const o=(0,e.makePoint)(n.pnt.len,n.pnt.sI,n.pnt.rI,n.pnt.cI);if(l(n,o,r).match){let e=i(n,o,r,t);if(e.done){let t=n.token(r.TOKEN,e.val,n.src.substring(n.pnt.sI,o.sI),o);return t.use={block:r.name},n.pnt.sI=o.sI,n.pnt.rI=o.rI,n.pnt.cI=o.cI,t}return e.bad||n.bad("invalid_text",n.pnt.sI,o.sI)}}}}}}})};function l(e,t,n){let r=e.src,l=t.sI,i=t.rI,o=t.cI,s=n.start||{},a=s.rule||{},c=null;a.parent&&(a.parent.include&&(c=a.parent.include.includes(e.ctx.rule.parent.name)&&(null===c||c)),a.parent.exclude&&(c=!a.parent.exclude.includes(e.ctx.rule.parent.name)&&(null===c||c))),a.current&&(a.current.include&&(c=a.current.include.includes(e.ctx.rule.name)&&(null===c||c)),a.current.exclude&&(c=!a.current.exclude.includes(e.ctx.rule.name)&&(null===c||c)));let u=""===a.state?"":a.state||"o";u&&(c=u.includes(e.ctx.rule.state)&&(null===c||c));let d=!0,p=s.fixed;if(c&&null!=p){d=!1,p=Array.isArray(p)?p:[p];for(let e=0;!d&&ee[0]),c=s.map(e=>e.substring(1)),u=n.escapeChar,d=t.sI,p=t.rI,f=t.cI,m=!1,h="",g=d,v=0;e:do{if(h=i[d],-1<(v=a.indexOf(h))){let e=c[v];if(void 0===e||""===e){g=d+1,m=!0;break e}if("string"==typeof e&&e===i.substring(d+1,d+1+e.length)){g=d+1+e.length,m=!0;break e}}if(u===h){let t=n.escape[i[d+1]];if(null!=t)h=t,d++,f++;else{if(!n.allowUnknownEscape)return{done:!1,val:"",bad:e.bad("invalid_escape",d,d+1)};h=n.preserveEscapeChar?i.substring(d,d+2):i[d+1],d++}}l.push(h),d++,f++,"\n"===h&&(p++,f=0)}while(d<=i.length);if(m){if(!1!==o.consume){let e=i.substring(d,g);if(!Array.isArray(o.consume)||o.consume.includes(e)){let e=d;for(;e{if("val"===e.ctx.rule.name)return{done:!0,token:void 0}}},number:{lex:!1},string:{lex:!0,chars:"'\"",abandon:!0},text:{lex:!1},comment:{def:{hash:{eatline:!0},slash:null,multi:null,semi:{line:!0,start:";",lex:!0,eatline:!0}}}});const{ZZ:r,ST:l,VL:i,OS:o,CS:s,CL:a,EQ:c,DOT:u,HV:d,HK:p,DK:f}=t.token,m=[p,l,i];t.rule("ini",e=>{e.bo(e=>{e.node={}}).open([{s:[o],p:"table",b:1},{s:[m,c],p:"table",b:2},{s:[d,o],p:"table",b:2},{s:[r]}])}),t.rule("table",e=>{e.bo(e=>{if(e.node=e.parent.node,e.prev.u.dive){let t=e.prev.u.dive;for(let n=0;n{Object.assign(e.node,e.child.node)}).close([{s:[o],r:"table",b:1},{s:[s],r:"table",a:e=>e.u.dive=e.child.u.dive},{s:[r]}])}),t.rule("dive",e=>{e.open([{s:[f,u],a:e=>(e.u.dive=e.parent.u.dive||[]).push(e.o0.val),p:"dive"},{s:[f],a:e=>(e.u.dive=e.parent.u.dive||[]).push(e.o0.val)}]).close([{s:[s],b:1}])}),t.rule("map",e=>{e.open([{s:[m,c],c:e=>"table"===e.parent.name,p:"pair",b:2},{s:[m],c:e=>"table"===e.parent.name,p:"pair",b:1}],{append:!0}).close([{s:[o],b:1},{s:[r]}])}),t.rule("pair",e=>{e.open([{s:[m,c],c:e=>"table"===e.parent.parent.name,p:"val",a:e=>{let t=""+e.o0.val;Array.isArray(e.node[t])?e.u.ini_array=e.node[t]:(e.u.key=t,2"table"===e.parent.parent.name,a:e=>{let t=e.o0.val;"string"==typeof t&&0"table"===e.parent.parent.name,e:e=>e.c1},{s:[m],b:1,r:"pair"},{s:[o],b:1}])}),t.rule("val",e=>{e.open([{s:[[o,s]],r:"val",u:{ini_prev:!0}},{s:[r],a:e=>e.node=""}],{custom:e=>e.filter(e=>"json,list"!==e.g.join())}).ac(e=>{if(l===e.o0.tin&&"'"===e.o0.src[0])try{e.node=JSON.parse(e.node)}catch(t){}null!=e.prev.u.ini_prev?e.prev.node=e.node=e.prev.o0.src+e.node:e.parent.u.ini_array&&e.parent.u.ini_array.push(e.node)})})},t})); \ No newline at end of file diff --git a/ini.ts b/ini.ts deleted file mode 100644 index 38c8ed6..0000000 --- a/ini.ts +++ /dev/null @@ -1,298 +0,0 @@ -/* Copyright (c) 2021-2025 Richard Rodger, MIT License */ - -// Import Jsonic types used by plugin. -import { Jsonic, RuleSpec, NormAltSpec, Lex } from 'jsonic' -import { Hoover } from '@jsonic/hoover' - -// TODO: use a src folder - -type IniOptions = {} - -function Ini(jsonic: Jsonic, _options: IniOptions) { - jsonic.use(Hoover, { - lex: { - order: 8.5e6, - }, - block: { - endofline: { - start: { - rule: { - parent: { - include: ['pair', 'elem'], - }, - }, - }, - end: { - fixed: ['\n', '\r\n', '#', ';', ''], - consume: ['\n', '\r\n'], - }, - escapeChar: '\\', - escape: { - '#': '#', - ';': ';', - '\\': '\\', - }, - preserveEscapeChar: true, - trim: true, - }, - key: { - token: '#HK', - start: { - rule: { - current: { - exclude: ['dive'], - }, - state: 'oc', - }, - }, - end: { - fixed: ['=', '\n', '\r\n', '#', ';', ''], - consume: false, - }, - escape: { - '#': '#', - ';': ';', - '\\': '\\', - }, - trim: true, - }, - divekey: { - token: '#DK', - start: { - rule: { - current: { - include: ['dive'], - }, - }, - }, - end: { - fixed: [']', '.'], - consume: false, - }, - escapeChar: '\\', - escape: { - ']': ']', - '.': '.', - '\\': '\\', - }, - allowUnknownEscape: true, - trim: true, - }, - }, - }) - - jsonic.options({ - rule: { - start: 'ini', - exclude: 'jsonic', - }, - lex: { - emptyResult: {}, - }, - fixed: { - token: { - '#EQ': '=', - '#DOT': '.', - '#OB': null, - '#CB': null, - '#CL': null, - }, - }, - - line: { - check: (lex: Lex) => { - if ('val' === lex.ctx.rule.name) { - return { done: true, token: undefined } - } - }, - }, - number: { - lex: false, - }, - string: { - lex: true, - chars: `'"`, - abandon: true, - }, - text: { - lex: false, - }, - - comment: { - def: { - hash: { eatline: true }, - slash: null, - multi: null, - semi: { line: true, start: ';', lex: true, eatline: true }, - }, - }, - }) - - const { ZZ, ST, VL, OS, CS, CL, EQ, DOT, HV, HK, DK } = jsonic.token - - const KEY = [HK, ST, VL] - - jsonic.rule('ini', (rs: RuleSpec) => { - rs.bo((r) => { - r.node = {} - }).open([ - { s: [OS], p: 'table', b: 1 }, - { s: [KEY, EQ], p: 'table', b: 2 }, - { s: [HV, OS], p: 'table', b: 2 }, - { s: [ZZ] }, - ]) - }) - - jsonic.rule('table', (rs: RuleSpec) => { - rs.bo((r) => { - r.node = r.parent.node - - if (r.prev.u.dive) { - let dive = r.prev.u.dive - for (let dI = 0; dI < dive.length; dI++) { - r.node = r.node[dive[dI]] = r.node[dive[dI]] || {} - } - } - }) - .open([ - { s: [OS], p: 'dive' }, - { s: [KEY, EQ], p: 'map', b: 2 }, - { s: [HV, OS], p: 'map', b: 2 }, - { s: [CS], p: 'map' }, - { s: [ZZ] }, - ]) - .bc((r) => { - Object.assign(r.node, r.child.node) - }) - .close([ - { s: [OS], r: 'table', b: 1 }, - { s: [CS], r: 'table', a: (r) => (r.u.dive = r.child.u.dive) }, - { s: [ZZ] }, - ]) - }) - - // TODO: maybe backport this to toml? - jsonic.rule('dive', (rs: RuleSpec) => { - rs.open([ - { - s: [DK, DOT], - a: (r) => (r.u.dive = r.parent.u.dive || []).push(r.o0.val), - p: 'dive', - }, - { - s: [DK], - a: (r) => (r.u.dive = r.parent.u.dive || []).push(r.o0.val), - }, - ]).close([{ s: [CS], b: 1 }]) - }) - - jsonic.rule('map', (rs: RuleSpec) => { - rs.open( - [ - // Pair from implicit map. - { - s: [KEY, EQ], - c: (r) => 'table' === r.parent.name, - p: 'pair', - b: 2, - }, - - { - s: [KEY], - c: (r) => 'table' === r.parent.name, - p: 'pair', - b: 1, - }, - ], - { append: true }, - ).close([{ s: [OS], b: 1 }, { s: [ZZ] }]) - }) - - jsonic.rule('pair', (rs: RuleSpec) => { - rs.open([ - { - s: [KEY, EQ], - c: (r) => 'table' === r.parent.parent.name, - p: 'val', - a: (r) => { - let key = '' + r.o0.val - if (Array.isArray(r.node[key])) { - r.u.ini_array = r.node[key] - } else { - r.u.key = key - if (2 < key.length && key.endsWith('[]')) { - key = r.u.key = key.slice(0, -2) - r.node[key] = r.u.ini_array = Array.isArray(r.node[key]) - ? r.node[key] - : undefined === r.node[key] - ? [] - : [r.node[key]] - } else { - r.u.pair = true - } - } - }, - }, - - // Special case: key by itself means key=true - { - s: [HK], - c: (r) => 'table' === r.parent.parent.name, - a: (r) => { - let key = r.o0.val - if ('string' === typeof key && 0 < key.length) { - r.parent.node[key] = true - } - }, - }, - ]).close([ - { - s: [KEY, CL], - c: (r) => 'table' === r.parent.parent.name, - e: (r) => { - return r.c1 - }, - }, - { s: [KEY], b: 1, r: 'pair' }, - { s: [OS], b: 1 }, - ]) - }) - - jsonic.rule('val', (rs: RuleSpec) => { - rs.open( - [ - // Since OS,CS are fixed tokens, concat them with string value - // if they appear as first char in a RHS value. - { - s: [[OS, CS]], - r: 'val', - u: { ini_prev: true }, - }, - - { s: [ZZ], a: (r) => (r.node = '') }, - ], - { - custom: (alts: NormAltSpec[]) => - alts.filter((alt: NormAltSpec) => alt.g.join() !== 'json,list'), - }, - ).ac((r) => { - if (ST === r.o0.tin && "'" === r.o0.src[0]) { - try { - r.node = JSON.parse(r.node) - } catch (e) { - // Invalid JSON, just accept val as given - } - } - - if (null != r.prev.u.ini_prev) { - r.prev.node = r.node = r.prev.o0.src + r.node - } else if (r.parent.u.ini_array) { - r.parent.u.ini_array.push(r.node) - } - }) - }) -} - -export { Ini } - -export type { IniOptions } diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 95393f9..0000000 --- a/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - transform: { - "^.+\\.tsx?$": "es-jest" - }, - testEnvironment: 'node', - testMatch: ['**/test/**/*.test.ts'], - watchPathIgnorePatterns: ['.*.js$'], -} diff --git a/package.json b/package.json index b6ba1f7..41761d5 100644 --- a/package.json +++ b/package.json @@ -2,17 +2,15 @@ "name": "@jsonic/ini", "version": "0.5.0", "description": "This plugin allows the [Jsonic](https://jsonic.senecajs.org) JSON parser to support INI syntax.", - "main": "ini.js", + "main": "dist/ini.js", "type": "commonjs", - "browser": "ini.min.js", - "types": "ini.d.ts", + "types": "dist/ini.d.ts", "homepage": "https://github.com/jsonicjs/ini", "keywords": [ - "pattern", - "matcher", - "object", - "property", - "json" + "json", + "jsonic", + "parser", + "ini" ], "author": "Richard Rodger (http://richardrodger.com)", "repository": { @@ -20,34 +18,27 @@ "url": "git://github.com/jsonicjs/ini.git" }, "scripts": { - "test": "jest --coverage", - "test-some": "jest -t", - "test-watch": "jest --coverage --watchAll", - "watch": "tsc -w -d", + "test": "node --enable-source-maps --test \"dist-test/*.test.js\"", + "test-some": "node --enable-source-maps --test-name-pattern=\"$npm_config_pattern\" --test \"dist-test/*.test.js\"", + "watch": "tsc --build src test -w", + "build": "tsc --build src test", + "clean": "rm -rf dist dist-test node_modules yarn.lock package-lock.json", "doc": "jsonic-doc", - "build": "tsc -d", - "prettier": "prettier --write --no-semi --single-quote *.ts test/*.js", - "clean": "rm -rf node_modules yarn.lock package-lock.json", "reset": "npm run clean && npm i && npm run build && npm test", "repo-tag": "REPO_VERSION=`node -e \"console.log(require('./package').version)\"` && echo TAG: v$REPO_VERSION && git commit -a -m v$REPO_VERSION && git push && git tag v$REPO_VERSION && git push --tags;", "repo-publish": "npm run clean && npm i && npm run repo-publish-quick", - "repo-publish-quick": "npm run prettier && npm run build && npm run test && npm run repo-tag && npm publish --access public --registry https://registry.npmjs.org " + "repo-publish-quick": "npm run build && npm run test && npm run doc && npm run repo-tag && npm publish --access public --registry https://registry.npmjs.org " }, "license": "MIT", "files": [ - "*.ts", - "*.js", - "*.map", + "src", + "dist", "LICENSE" ], "devDependencies": { + "@hapi/code": "^9.0.3", "@jsonic/doc": "^0.0.9", - "@types/jest": "^30.0.0", - "es-jest": "^2.1.0", - "esbuild": "^0.25.11", - "ini": "^6.0.0", - "jest": "^30.2.0", - "prettier": "^3.6.2", + "@types/node": "^25.4.0", "typescript": "^5.9.3" }, "peerDependencies": { diff --git a/src/ini.ts b/src/ini.ts new file mode 100644 index 0000000..b44ed26 --- /dev/null +++ b/src/ini.ts @@ -0,0 +1,542 @@ +/* Copyright (c) 2021-2025 Richard Rodger, MIT License */ + +// Import Jsonic types used by plugin. +import { Jsonic, RuleSpec, NormAltSpec, Lex, makePoint, Token } from 'jsonic' +import { Hoover } from '@jsonic/hoover' + +type InlineCommentOptions = { + // Whether inline comments are active. Default: false. + active?: boolean + // Characters that start an inline comment. Default: ['#', ';']. + chars?: string[] + // Escape mechanisms for literal comment characters in values. + escape?: { + // Allow \; and \# to produce literal ; and #. Default: true. + backslash?: boolean + // Require whitespace before comment char to trigger. Default: false. + whitespace?: boolean + } +} + +type IniOptions = { + multiline?: { + // Character before newline indicating continuation. Default: '\\'. + // Set to false to disable backslash continuation. + continuation?: string | false + // When true, a continuation line must be indented (leading whitespace). + // Indented lines continue the previous value even without a continuation char. + indent?: boolean + } | boolean + section?: { + // How to handle duplicate section headers. Default: 'merge'. + // 'merge': combine keys from all occurrences (last value wins for duplicate keys) + // 'override': last section occurrence replaces earlier ones entirely + // 'error': throw when a previously declared section header appears again + duplicate?: 'merge' | 'override' | 'error' + } + comment?: { + // Control inline comment behavior. Default: inactive. + inline?: InlineCommentOptions + } +} + +function Ini(jsonic: Jsonic, _options: IniOptions) { + // Resolve inline comment options. + const inlineComment = { + active: _options.comment?.inline?.active ?? false, + chars: _options.comment?.inline?.chars ?? ['#', ';'], + escape: { + backslash: _options.comment?.inline?.escape?.backslash ?? true, + whitespace: _options.comment?.inline?.escape?.whitespace ?? false, + }, + } + + // Build Hoover end.fixed arrays based on inline comment config. + // When active without whitespace mode, include comment chars as terminators. + // When whitespace mode is on, the custom value matcher handles detection instead. + const inlineCharsInFixed = + inlineComment.active && !inlineComment.escape.whitespace + + const eolEndFixed: string[] = ['\n', '\r\n'] + if (inlineCharsInFixed) { + eolEndFixed.push(...inlineComment.chars) + } + eolEndFixed.push('') + + const keyEndFixed: string[] = ['=', '\n', '\r\n'] + if (inlineCharsInFixed) { + keyEndFixed.push(...inlineComment.chars) + } + keyEndFixed.push('') + + // Build escape maps. Always include '\\' -> '\\'. + // Add comment char escapes when inline comments are active with backslash escaping. + const eolEscape: Record = { '\\': '\\' } + const keyEscape: Record = { '\\': '\\' } + if (inlineComment.active && inlineComment.escape.backslash) { + for (const ch of inlineComment.chars) { + eolEscape[ch] = ch + keyEscape[ch] = ch + } + } + + jsonic.use(Hoover, { + lex: { + order: 8.5e6, + }, + block: { + endofline: { + start: { + rule: { + parent: { + include: ['pair', 'elem'], + }, + }, + }, + end: { + fixed: eolEndFixed, + consume: ['\n', '\r\n'], + }, + escapeChar: '\\', + escape: eolEscape, + allowUnknownEscape: true, + preserveEscapeChar: true, + trim: true, + }, + key: { + token: '#HK', + start: { + rule: { + current: { + exclude: ['dive'], + }, + state: 'oc', + }, + }, + end: { + fixed: keyEndFixed, + consume: false, + }, + escape: keyEscape, + trim: true, + }, + divekey: { + token: '#DK', + start: { + rule: { + current: { + include: ['dive'], + }, + }, + }, + end: { + fixed: [']', '.'], + consume: false, + }, + escapeChar: '\\', + escape: { + ']': ']', + '.': '.', + '\\': '\\', + }, + allowUnknownEscape: true, + trim: true, + }, + }, + }) + + jsonic.options({ + rule: { + start: 'ini', + exclude: 'jsonic', + }, + lex: { + emptyResult: {}, + }, + fixed: { + token: { + '#EQ': '=', + '#DOT': '.', + '#OB': null, + '#CB': null, + '#CL': null, + }, + }, + + line: { + check: (lex: Lex) => { + if ('val' === lex.ctx.rule.name) { + return { done: true, token: undefined } + } + }, + }, + number: { + lex: false, + }, + string: { + lex: true, + chars: `'"`, + abandon: true, + }, + text: { + lex: false, + }, + + comment: { + def: { + hash: { eatline: true }, + slash: null, + multi: null, + semi: { line: true, start: ';', lex: true, eatline: true }, + }, + }, + }) + + // Custom value lex matcher. + // Needed when: (a) multiline continuation is enabled, or + // (b) inline comments are active with whitespace-prefix detection. + // Runs at higher priority than Hoover's endofline block to intercept values. + const multiline = true === _options.multiline ? {} : _options.multiline + const needCustomMatcher = + !!multiline || (inlineComment.active && inlineComment.escape.whitespace) + + if (needCustomMatcher) { + const continuation: string | false = multiline + ? (multiline.continuation !== undefined ? multiline.continuation : '\\') + : false + const indent = multiline ? (multiline.indent || false) : false + const HV_TIN = jsonic.token('#HV') as number + + // Build a Set for fast comment char lookup in the matcher. + const commentCharSet = new Set(inlineComment.chars) + + jsonic.options({ + lex: { + match: { + multiline: { + // Lower order than Hoover (8.5e6) so this runs first. + order: 8.4e6, + make: () => { + return function multilineMatcher(lex: Lex): Token | undefined { + // Only match in value context during rule open state + // (same as Hoover endofline block, which defaults to state 'o'). + let ctx = (lex as any).ctx + let parentName = ctx?.rule?.parent?.name + if (parentName !== 'pair' && parentName !== 'elem') { + return undefined + } + if (ctx?.rule?.state !== 'o') { + return undefined + } + + let src = lex.src + let sI = lex.pnt.sI + let rI = lex.pnt.rI + let cI = lex.pnt.cI + let startI = sI + let chars: string[] = [] + + while (sI < src.length) { + let c = src[sI] + + // Check for inline comment characters (end value). + if (inlineComment.active && commentCharSet.has(c)) { + if (inlineComment.escape.whitespace) { + // Only treat as comment if preceded by whitespace. + if ( + chars.length > 0 && + (chars[chars.length - 1] === ' ' || + chars[chars.length - 1] === '\t') + ) { + break + } + // Not preceded by whitespace: treat as literal. + chars.push(c) + sI++; cI++ + continue + } + break + } + + // Check for backslash continuation before newline. + if (false !== continuation && c === continuation) { + if (src[sI + 1] === '\n') { + // \ continuation + sI += 2; rI++; cI = 0 + // Consume leading whitespace on continuation line. + while (sI < src.length && + (src[sI] === ' ' || src[sI] === '\t')) { + sI++; cI++ + } + continue + } + if (src[sI + 1] === '\r' && src[sI + 2] === '\n') { + // \ continuation + sI += 3; rI++; cI = 0 + while (sI < src.length && + (src[sI] === ' ' || src[sI] === '\t')) { + sI++; cI++ + } + continue + } + } + + // Check for newline. + if (c === '\n' || (c === '\r' && src[sI + 1] === '\n')) { + // Indent continuation: next line starts with whitespace. + if (indent) { + let nextI = c === '\r' ? sI + 2 : sI + 1 + if (nextI < src.length && + (src[nextI] === ' ' || src[nextI] === '\t')) { + rI++; cI = 0 + sI = nextI + // Consume leading whitespace. + while (sI < src.length && + (src[sI] === ' ' || src[sI] === '\t')) { + sI++; cI++ + } + chars.push(' ') + continue + } + } + + // Normal newline: end value and consume the newline. + if (c === '\r') { sI += 2 } else { sI++ } + rI++; cI = 0 + break + } + + // Handle escape sequences. + if (c === '\\' && sI + 1 < src.length) { + let next = src[sI + 1] + if ( + inlineComment.active && + inlineComment.escape.backslash && + commentCharSet.has(next) + ) { + chars.push(next) + sI += 2; cI += 2 + continue + } + if (next === '\\') { + chars.push('\\') + sI += 2; cI += 2 + continue + } + } + + chars.push(c) + sI++; cI++ + } + + let val: string | undefined = chars.join('').trim() + + let pnt = makePoint(lex.pnt.len, sI, rI, cI) + let tkn = lex.token( + HV_TIN, val, src.substring(startI, sI), pnt) + tkn.use = { block: 'endofline' } + + lex.pnt.sI = sI + lex.pnt.rI = rI + lex.pnt.cI = cI + + return tkn + } + } + } + } + } + }) + } + + const { ZZ, ST, VL, OS, CS, CL, EQ, DOT, HV, HK, DK } = jsonic.token + + const KEY = [HK, ST, VL] + + const dupSection = _options.section?.duplicate || 'merge' + + // Track explicitly declared section paths per parse call. + // Cleared in the ini rule's bo handler, used in the table rule. + const declaredSections = new Set() + + jsonic.rule('ini', (rs: RuleSpec) => { + rs.bo((r) => { + r.node = {} + declaredSections.clear() + }).open([ + { s: [OS], p: 'table', b: 1 }, + { s: [KEY, EQ], p: 'table', b: 2 }, + { s: [HV, OS], p: 'table', b: 2 }, + { s: [ZZ] }, + ]) + }) + + jsonic.rule('table', (rs: RuleSpec) => { + rs.bo((r) => { + r.node = r.parent.node + + if (r.prev.u.dive) { + let dive = r.prev.u.dive + // Use null char as separator to avoid collisions with dots in key names. + let sectionKey = dive.join('\x00') + let isDuplicate = declaredSections.has(sectionKey) + + if (isDuplicate && dupSection === 'error') { + throw new Error( + 'Duplicate section: [' + dive.join('.') + ']' + ) + } + + for (let dI = 0; dI < dive.length; dI++) { + if (dI === dive.length - 1 && isDuplicate && dupSection === 'override') { + // Override: replace the section object entirely. + r.node = r.node[dive[dI]] = {} + } else { + r.node = r.node[dive[dI]] = r.node[dive[dI]] || {} + } + } + + declaredSections.add(sectionKey) + } + }) + .open([ + { s: [OS], p: 'dive' }, + { s: [KEY, EQ], p: 'map', b: 2 }, + { s: [HV, OS], p: 'map', b: 2 }, + { s: [CS], p: 'map' }, + { s: [ZZ] }, + ]) + .bc((r) => { + Object.assign(r.node, r.child.node) + }) + .close([ + { s: [OS], r: 'table', b: 1 }, + { s: [CS], r: 'table', a: (r) => (r.u.dive = r.child.u.dive) }, + { s: [ZZ] }, + ]) + }) + + jsonic.rule('dive', (rs: RuleSpec) => { + rs.open([ + { + s: [DK, DOT], + a: (r) => (r.u.dive = r.parent.u.dive || []).push(r.o0.val), + p: 'dive', + }, + { + s: [DK], + a: (r) => (r.u.dive = r.parent.u.dive || []).push(r.o0.val), + }, + ]).close([{ s: [CS], b: 1 }]) + }) + + jsonic.rule('map', (rs: RuleSpec) => { + rs.open( + [ + // Pair from implicit map. + { + s: [KEY, EQ], + c: (r) => 'table' === r.parent.name, + p: 'pair', + b: 2, + }, + + { + s: [KEY], + c: (r) => 'table' === r.parent.name, + p: 'pair', + b: 1, + }, + ], + { append: true }, + ).close([{ s: [OS], b: 1 }, { s: [ZZ] }]) + }) + + jsonic.rule('pair', (rs: RuleSpec) => { + rs.open([ + { + s: [KEY, EQ], + c: (r) => 'table' === r.parent.parent.name, + p: 'val', + a: (r) => { + let key = '' + r.o0.val + if (Array.isArray(r.node[key])) { + r.u.ini_array = r.node[key] + } else { + r.u.key = key + if (2 < key.length && key.endsWith('[]')) { + key = r.u.key = key.slice(0, -2) + r.node[key] = r.u.ini_array = Array.isArray(r.node[key]) + ? r.node[key] + : undefined === r.node[key] + ? [] + : [r.node[key]] + } else { + r.u.pair = true + } + } + }, + }, + + // Special case: key by itself means key=true + { + s: [HK], + c: (r) => 'table' === r.parent.parent.name, + a: (r) => { + let key = r.o0.val + if ('string' === typeof key && 0 < key.length) { + r.parent.node[key] = true + } + }, + }, + ]).close([ + { + s: [KEY, CL], + c: (r) => 'table' === r.parent.parent.name, + e: (r) => { + return r.c1 + }, + }, + { s: [KEY], b: 1, r: 'pair' }, + { s: [OS], b: 1 }, + ]) + }) + + jsonic.rule('val', (rs: RuleSpec) => { + rs.open( + [ + // Since OS,CS are fixed tokens, concat them with string value + // if they appear as first char in a RHS value. + { + s: [[OS, CS]], + r: 'val', + u: { ini_prev: true }, + }, + + { s: [ZZ], a: (r) => (r.node = '') }, + ], + { + custom: (alts: NormAltSpec[]) => + alts.filter((alt: NormAltSpec) => alt.g.join() !== 'json,list'), + }, + ).ac((r) => { + if (ST === r.o0.tin && "'" === r.o0.src[0]) { + try { + r.node = JSON.parse(r.node) + } catch (e) { + // Invalid JSON, just accept val as given + } + } + + if (null != r.prev.u.ini_prev) { + r.prev.node = r.node = r.prev.o0.src + r.node + } else if (r.parent.u.ini_array) { + r.parent.u.ini_array.push(r.node) + } + }) + }) +} + +export { Ini } + +export type { IniOptions, InlineCommentOptions } diff --git a/tsconfig.json b/src/tsconfig.json similarity index 51% rename from tsconfig.json rename to src/tsconfig.json index 6070411..da88ae7 100644 --- a/tsconfig.json +++ b/src/tsconfig.json @@ -1,18 +1,14 @@ { "compilerOptions": { - "isolatedModules": true, "esModuleInterop": true, "module": "nodenext", - "moduleResolution": "nodenext", "noEmitOnError": true, - "noImplicitAny": true, + "outDir": "../dist", + "rootDir": ".", + "declaration": true, "resolveJsonModule": true, "sourceMap": true, "strict": true, - "target": "ES2019" - }, - "exclude": [ - "dist", - "node_modules" - ] + "target": "ES2021" + } } diff --git a/test/ini.test.d.ts b/test/ini.test.d.ts deleted file mode 100644 index cb0ff5c..0000000 --- a/test/ini.test.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/test/ini.test.js b/test/ini.test.js deleted file mode 100644 index b200c20..0000000 --- a/test/ini.test.js +++ /dev/null @@ -1,229 +0,0 @@ -"use strict"; -/* Copyright (c) 2021-2025 Richard Rodger and other contributors, MIT License */ -Object.defineProperty(exports, "__esModule", { value: true }); -const jsonic_1 = require("jsonic"); -const ini_1 = require("../ini"); -const j = jsonic_1.Jsonic.make().use(ini_1.Ini); -describe('ini', () => { - test('happy', () => { - expect(j('a=1')).toEqual({ a: "1" }); - expect(j('[A]')).toEqual({ A: {} }); - expect(j(`[A.B]\nc='2'`)).toEqual({ A: { B: { c: 2 } } }); - expect(j('a[]=1\na[]=2')).toEqual({ a: ['1', '2'] }); - expect(j('a=\nb=')).toEqual({ a: '', b: '' }); - expect(j(';X\n#Y\na=1;2\nb=2')).toEqual({ a: '1', b: '2' }); - }); - test('basic', () => { - expect(j(` -; comment -a = 1 -b = x -c = y y -c0 = true -" c1 c2 " = null -'[]'='[]' - -[d] -e = 2 -e0[]=q q -e0[]=w w -"[]"="[]" - -[f] -# x:11 -g = 'G' -# x:12 - - -[h.i] -j = [3,4] -j0 = ]3,4[ -k = false - -[l.m.n.o] -p = "P" -q = {x:1} -u = v = 5 -w = '{"y":{"z":6}}' -aa = 7 - -`)) - .toEqual({ - a: '1', - b: 'x', - c: 'y y', - c0: true, - ' c1 c2 ': null, - '[]': [], - d: { - e: '2', - e0: ['q q', 'w w'], - '[]': '[]', - }, - f: { g: 'G' }, - h: { i: { j: '[3,4]', j0: ']3,4[', k: false } }, - l: { - m: { - n: { - o: { - p: 'P', - q: '{x:1}', - u: 'v = 5', - w: { y: { z: 6 } }, - aa: '7' - }, - } - } - } - }); - }); - // NOTE: Copyright (c) Isaac Z. Schlueter and Contributors, ISC License - test('ini-module-test', () => { - expect(j(` -o = p - -a with spaces = b c - -; wrap in quotes to JSON-decode and preserve spaces -" xa n p " = "\\"\\r\\nyoyoyo\\r\\r\\n" - -; wrap in quotes to get a key with a bracket, not a section. -"[disturbing]" = hey you never know - -; Test single quotes -s = 'something' - -; Test mixing quotes - -s1 = "something' - -; Test double quotes -s2 = "something else" - -; Test blank value -s3 = - -; Test value with only spaces -s4 = - -; Test quoted value with only spaces -s5 = ' ' - -; Test quoted value with leading and trailing spaces -s6 = ' a ' - -; Test no equal sign -s7 - -; Test bool(true) -true = true - -; Test bool(false) -false = false - -; Test null -null = null - -; Test undefined -undefined = undefined - -; Test arrays -zr[] = deedee -ar[] = one -ar[] = three -; This should be included in the array -ar = this is included - -; Test resetting of a value (and not turn it into an array) -br = cold -br = warm - -eq = "eq=eq" - -; a section -[a] -av = a val -e = { o: p, a: { av: a val, b: { c: { e: 'this [value]' } } } } -j = "{ o: \\"p\\", a: { av: \\"a val\\", b: { c: { e: \\"this [value]\\" } } } }" -"[]" = a square? - -; Nested array -cr[] = four -cr[] = eight - -; nested child without middle parent -; should create otherwise-empty a.b -[a.b.c] -e = 1 -j = 2 - -; dots in the section name should be literally interpreted -[x\\.y\\.z] -x.y.z = xyz - -[x\\.y\\.z.a\\.b\\.c] -a.b.c = abc - -; this next one is not a comment! it's escaped! -nocomment = this\\; this is not a comment - -# Support the use of the number sign (#) as an alternative to the semicolon for indicating comments. -# http://en.wikipedia.org/wiki/INI_file#Comments - -# this next one is not a comment! it's escaped! -noHashComment = this\\# this is not a comment`)) - .toEqual({ - " xa n p ": "\"\r\nyoyoyo\r\r\n", - "[disturbing]": "hey you never know", - "a": { - "[]": "a square?", - "av": "a val", - "b": { - "c": { - "e": "1", - "j": "2", - }, - }, - "cr": [ - "four", - "eight", - ], - "e": "{ o: p, a: { av: a val, b: { c: { e: 'this [value]' } } } }", - "j": "{ o: \"p\", a: { av: \"a val\", b: { c: { e: \"this [value]\" } } } }", - }, - "a with spaces": "b c", - "ar": [ - "one", - "three", - "this is included", - ], - "br": "warm", - "eq": "eq=eq", - "false": false, - "null": null, - "o": "p", - "s": "something", - "s1": "\"something'", - "s2": "something else", - "s3": "", - "s4": "", - "s5": " ", - "s6": " a ", - "s7": true, - "true": true, - "undefined": "undefined", - "x.y.z": { - "a.b.c": { - "a.b.c": "abc", - "nocomment": "this; this is not a comment", - "noHashComment": "this# this is not a comment", - }, - "x.y.z": "xyz", - }, - "zr": [ - "deedee", - ], - }); - }); -}); -//# sourceMappingURL=ini.test.js.map \ No newline at end of file diff --git a/test/ini.test.js.map b/test/ini.test.js.map deleted file mode 100644 index ad48bfb..0000000 --- a/test/ini.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ini.test.js","sourceRoot":"","sources":["ini.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/test/ini.test.ts b/test/ini.test.ts index 50675fc..c06b039 100644 --- a/test/ini.test.ts +++ b/test/ini.test.ts @@ -1,8 +1,10 @@ /* Copyright (c) 2021-2025 Richard Rodger and other contributors, MIT License */ +import { test, describe } from 'node:test' +import { expect } from '@hapi/code' import { Jsonic } from 'jsonic' -import { Ini } from '../ini' +import { Ini } from '../dist/ini' const j = Jsonic.make().use(Ini) @@ -11,12 +13,13 @@ const j = Jsonic.make().use(Ini) describe('ini', () => { test('happy', () => { - expect(j('a=1')).toEqual({ a: "1" }) - expect(j('[A]')).toEqual({ A: {} }) - expect(j(`[A.B]\nc='2'`)).toEqual({ A: { B: { c: 2 } } }) - expect(j('a[]=1\na[]=2')).toEqual({ a: ['1', '2'] }) - expect(j('a=\nb=')).toEqual({ a: '', b: '' }) - expect(j(';X\n#Y\na=1;2\nb=2')).toEqual({ a: '1', b: '2' }) + expect(j('a=1')).equal({ a: "1" }) + expect(j('[A]')).equal({ A: {} }) + expect(j(`[A.B]\nc='2'`)).equal({ A: { B: { c: 2 } } }) + expect(j('a[]=1\na[]=2')).equal({ a: ['1', '2'] }) + expect(j('a=\nb=')).equal({ a: '', b: '' }) + // Inline comments are off by default; ; and # mid-value are literal. + expect(j(';X\n#Y\na=1;2\nb=2')).equal({ a: '1;2', b: '2' }) }) @@ -26,7 +29,7 @@ describe('ini', () => { a = 1 b = x c = y y -c0 = true +c0 = true " c1 c2 " = null '[]'='[]' @@ -55,7 +58,7 @@ w = '{"y":{"z":6}}' aa = 7 `)) - .toEqual({ + .equal({ a: '1', b: 'x', c: 'y y', @@ -112,7 +115,7 @@ s2 = "something else" s3 = ; Test value with only spaces -s4 = +s4 = ; Test quoted value with only spaces s5 = ' ' @@ -180,7 +183,7 @@ nocomment = this\\; this is not a comment # this next one is not a comment! it's escaped! noHashComment = this\\# this is not a comment`)) - .toEqual({ + .equal({ " xa n p ": "\"\r\nyoyoyo\r\r\n", "[disturbing]": "hey you never know", "a": { @@ -223,8 +226,8 @@ noHashComment = this\\# this is not a comment`)) "x.y.z": { "a.b.c": { "a.b.c": "abc", - "nocomment": "this; this is not a comment", - "noHashComment": "this# this is not a comment", + "nocomment": "this\\; this is not a comment", + "noHashComment": "this\\# this is not a comment", }, "x.y.z": "xyz", }, @@ -238,3 +241,405 @@ noHashComment = this\\# this is not a comment`)) }) +describe('multiline', () => { + + test('backslash-continuation', () => { + const jm = Jsonic.make().use(Ini, { multiline: true }) + + // Basic continuation with \ + expect(jm('a = hello \\\nworld')).equal({ a: 'hello world' }) + + // Continuation with leading whitespace on next line (consumed) + expect(jm('a = hello \\\n world')).equal({ a: 'hello world' }) + + // Multiple continuations + expect(jm('a = one \\\ntwo \\\nthree')).equal({ a: 'one two three' }) + + // No continuation: normal newline ends value + expect(jm('a = hello\nb = world')).equal({ a: 'hello', b: 'world' }) + + // Continuation with \ + expect(jm('a = hello \\\r\nworld')).equal({ a: 'hello world' }) + + // Escaped backslash before newline is NOT continuation + expect(jm('a = path\\\\\nb = next')).equal({ a: 'path\\', b: 'next' }) + + // Continuation in a section + expect(jm('[s]\na = hello \\\n world')).equal({ s: { a: 'hello world' } }) + + // Empty value with continuation + expect(jm('a = \\\nworld')).equal({ a: 'world' }) + + // Inline comments off by default: ; is literal in value + expect(jm('a = hello \\\nworld ;not-a-comment\nb = 2')) + .equal({ a: 'hello world ;not-a-comment', b: '2' }) + }) + + test('indent-continuation', () => { + const ji = Jsonic.make().use(Ini, { multiline: { indent: true, continuation: false } }) + + // Indented line continues previous value + expect(ji('a = hello\n world')).equal({ a: 'hello world' }) + + // Multiple indent continuations + expect(ji('a = line1\n line2\n line3')).equal({ a: 'line1 line2 line3' }) + + // Non-indented line is a new key + expect(ji('a = hello\nb = world')).equal({ a: 'hello', b: 'world' }) + + // Tab indent + expect(ji('a = hello\n\tworld')).equal({ a: 'hello world' }) + + // Indent continuation in section + expect(ji('[s]\na = hello\n world')) + .equal({ s: { a: 'hello world' } }) + }) + + test('multiline-with-boolean-option', () => { + // multiline: true enables defaults (backslash continuation, no indent) + const jm = Jsonic.make().use(Ini, { multiline: true }) + expect(jm('a = hello \\\nworld')).equal({ a: 'hello world' }) + }) + + test('multiline-both-modes', () => { + // Both continuation char and indent enabled + const jb = Jsonic.make().use(Ini, { + multiline: { continuation: '\\', indent: true } + }) + + // Backslash continuation works + expect(jb('a = hello \\\nworld')).equal({ a: 'hello world' }) + + // Indent continuation also works + expect(jb('a = hello\n world')).equal({ a: 'hello world' }) + }) + + test('multiline-escapes', () => { + // Multiline with inline comments active and backslash escaping + const jm = Jsonic.make().use(Ini, { + multiline: true, + comment: { inline: { active: true, escape: { backslash: true } } }, + }) + + // Escaped comment chars still work with continuation + expect(jm('a = one\\; two \\\nthree')) + .equal({ a: 'one; two three' }) + + // Escaped hash + expect(jm('a = one\\# two \\\nthree')) + .equal({ a: 'one# two three' }) + }) + + test('multiline-no-inline-comments', () => { + // Multiline without inline comments: ; and # are literal + const jm = Jsonic.make().use(Ini, { multiline: true }) + + expect(jm('a = one; two \\\nthree')) + .equal({ a: 'one; two three' }) + + expect(jm('a = one# two \\\nthree')) + .equal({ a: 'one# two three' }) + }) +}) + + +describe('section-duplicate', () => { + + test('merge-default', () => { + const j = Jsonic.make().use(Ini) + + // Default: merge keys from duplicate sections + expect(j('[a]\nx=1\ny=2\n[a]\nz=3')) + .equal({ a: { x: '1', y: '2', z: '3' } }) + + // Duplicate key: last value wins + expect(j('[a]\nx=1\n[a]\nx=2')) + .equal({ a: { x: '2' } }) + + // Nested duplicate sections merge + expect(j('[a.b]\nx=1\n[a.b]\ny=2')) + .equal({ a: { b: { x: '1', y: '2' } } }) + + // Intermediate path preserved when merging + expect(j('[a.b]\nx=1\n[a]\ny=2')) + .equal({ a: { b: { x: '1' }, y: '2' } }) + }) + + test('merge-explicit', () => { + const jm = Jsonic.make().use(Ini, { section: { duplicate: 'merge' } }) + + expect(jm('[a]\nx=1\n[a]\ny=2')) + .equal({ a: { x: '1', y: '2' } }) + }) + + test('override', () => { + const jo = Jsonic.make().use(Ini, { section: { duplicate: 'override' } }) + + // Second occurrence replaces first + expect(jo('[a]\nx=1\ny=2\n[a]\nz=3')) + .equal({ a: { z: '3' } }) + + // First occurrence works normally + expect(jo('[a]\nx=1')) + .equal({ a: { x: '1' } }) + + // Override clears subsections too + expect(jo('[a.b]\nx=1\n[a]\ny=2\n[a]\nz=3')) + .equal({ a: { z: '3' } }) + + // Non-duplicate sections unaffected + expect(jo('[a]\nx=1\n[b]\ny=2')) + .equal({ a: { x: '1' }, b: { y: '2' } }) + + // Nested override + expect(jo('[a.b]\nx=1\n[a.b]\ny=2')) + .equal({ a: { b: { y: '2' } } }) + }) + + test('error', () => { + const je = Jsonic.make().use(Ini, { section: { duplicate: 'error' } }) + + // Single section: no error + expect(je('[a]\nx=1')).equal({ a: { x: '1' } }) + + // Multiple distinct sections: no error + expect(je('[a]\nx=1\n[b]\ny=2')) + .equal({ a: { x: '1' }, b: { y: '2' } }) + + // Duplicate section: throws + expect(() => je('[a]\nx=1\n[a]\ny=2')) + .to.throw(/Duplicate section/) + + // Duplicate nested section: throws + expect(() => je('[a.b]\nx=1\n[a.b]\ny=2')) + .to.throw(/Duplicate section/) + + // Intermediate path is NOT a declared section + // [a.b] creates intermediate [a] but does not declare it + expect(je('[a.b]\nx=1\n[a]\ny=2')) + .equal({ a: { b: { x: '1' }, y: '2' } }) + }) +}) + + +describe('inline-comment', () => { + + test('off-by-default', () => { + // Default: inline comments are off. ; and # mid-value are literal. + const j = Jsonic.make().use(Ini) + + expect(j('a = hello ; world')).equal({ a: 'hello ; world' }) + expect(j('a = hello # world')).equal({ a: 'hello # world' }) + expect(j('a = x;y;z')).equal({ a: 'x;y;z' }) + + // Line-start comments still work + expect(j('; comment\na = 1')).equal({ a: '1' }) + expect(j('# comment\na = 1')).equal({ a: '1' }) + }) + + test('active-basic', () => { + // Inline comments active with defaults (chars: ['#', ';']) + const j = Jsonic.make().use(Ini, { + comment: { inline: { active: true } }, + }) + + expect(j('a = hello ; comment')).equal({ a: 'hello' }) + expect(j('a = hello # comment')).equal({ a: 'hello' }) + expect(j('a = x;y')).equal({ a: 'x' }) + expect(j('a = value\nb = other')).equal({ a: 'value', b: 'other' }) + }) + + test('custom-chars', () => { + // Only ; is an inline comment char, not # + const j = Jsonic.make().use(Ini, { + comment: { inline: { active: true, chars: [';'] } }, + }) + + expect(j('a = hello ; comment')).equal({ a: 'hello' }) + expect(j('a = hello # not a comment')).equal({ a: 'hello # not a comment' }) + }) + + test('backslash-escape', () => { + // Backslash escaping enabled (default when active) + const j = Jsonic.make().use(Ini, { + comment: { inline: { active: true, escape: { backslash: true } } }, + }) + + expect(j('a = hello\\; world')).equal({ a: 'hello; world' }) + expect(j('a = hello\\# world')).equal({ a: 'hello# world' }) + expect(j('a = x\\;y ; comment')).equal({ a: 'x;y' }) + }) + + test('backslash-escape-disabled', () => { + // Backslash escaping explicitly disabled: \; keeps both chars but + // the escapeChar still prevents ; from terminating. The difference + // is that backslash is preserved in the output rather than consumed. + const j = Jsonic.make().use(Ini, { + comment: { inline: { active: true, escape: { backslash: false } } }, + }) + + // \; → \; (backslash preserved, ; did not terminate) + expect(j('a = hello\\; world')).equal({ a: 'hello\\; world' }) + + // Unescaped ; still terminates + expect(j('a = hello ; comment')).equal({ a: 'hello' }) + }) + + test('whitespace-prefix', () => { + // Whitespace-prefix mode: only treat as comment if preceded by whitespace + const j = Jsonic.make().use(Ini, { + comment: { inline: { active: true, escape: { whitespace: true } } }, + }) + + // No whitespace before ; → literal + expect(j('a = x;y;z')).equal({ a: 'x;y;z' }) + + // Whitespace before ; → inline comment + expect(j('a = hello ;comment')).equal({ a: 'hello' }) + expect(j('a = hello\t;comment')).equal({ a: 'hello' }) + + // Same for # + expect(j('a = x#y')).equal({ a: 'x#y' }) + expect(j('a = hello #comment')).equal({ a: 'hello' }) + }) + + test('whitespace-prefix-with-backslash', () => { + // Both whitespace and backslash escaping + const j = Jsonic.make().use(Ini, { + comment: { + inline: { + active: true, + escape: { whitespace: true, backslash: true }, + }, + }, + }) + + // No whitespace: literal + expect(j('a = x;y')).equal({ a: 'x;y' }) + + // Whitespace present: comment + expect(j('a = hello ;comment')).equal({ a: 'hello' }) + + // Backslash escape overrides whitespace: literal + expect(j('a = hello \\;not-a-comment')).equal({ a: 'hello ;not-a-comment' }) + }) + + test('with-multiline', () => { + // Inline comments active + multiline continuation + const j = Jsonic.make().use(Ini, { + multiline: true, + comment: { inline: { active: true } }, + }) + + // Comment terminates continued value + expect(j('a = hello \\\nworld ;comment\nb = 2')) + .equal({ a: 'hello world', b: '2' }) + + // Escaped comment char in multiline value + expect(j('a = hello\\; \\\nworld')) + .equal({ a: 'hello; world' }) + }) + + test('with-sections', () => { + const j = Jsonic.make().use(Ini, { + comment: { inline: { active: true } }, + }) + + expect(j('[s]\na = val ; comment\nb = other')) + .equal({ s: { a: 'val', b: 'other' } }) + }) + + test('line-comments-always-work', () => { + // Line-start comments work regardless of inline comment setting + const jOff = Jsonic.make().use(Ini) + const jOn = Jsonic.make().use(Ini, { + comment: { inline: { active: true } }, + }) + + const input = '; line comment\n# hash comment\na = 1' + expect(jOff(input)).equal({ a: '1' }) + expect(jOn(input)).equal({ a: '1' }) + }) +}) + + +describe('number-lex', () => { + + // Enable number lexing via post-config so Jsonic parses numeric values as numbers + function makeWithNumbers() { + const jn = Jsonic.make().use(Ini) + jn.options({ number: { lex: true } }) + return jn + } + + test('integers', () => { + const jn = makeWithNumbers() + + expect(jn('a=1')).equal({ a: 1 }) + expect(jn('a=0')).equal({ a: 0 }) + expect(jn('a=-3')).equal({ a: -3 }) + expect(jn('a=+2')).equal({ a: 2 }) + expect(jn('a=42\nb=99')).equal({ a: 42, b: 99 }) + }) + + test('floats', () => { + const jn = makeWithNumbers() + + expect(jn('a=2.5')).equal({ a: 2.5 }) + expect(jn('a=0.0')).equal({ a: 0 }) + expect(jn('a=-1.25')).equal({ a: -1.25 }) + }) + + test('scientific-notation', () => { + const jn = makeWithNumbers() + + expect(jn('a=1e10')).equal({ a: 1e10 }) + }) + + test('hex', () => { + const jn = makeWithNumbers() + + expect(jn('a=0xFF')).equal({ a: 255 }) + }) + + test('mixed-types', () => { + const jn = makeWithNumbers() + + // Numbers and strings coexist + expect(jn('a=1\nb=hello\nc=2.5\nd=true')) + .equal({ a: 1, b: 'hello', c: 2.5, d: true }) + + // Non-numeric strings stay as strings + expect(jn('a=1abc')).equal({ a: '1abc' }) + + // Empty value stays as empty string + expect(jn('a=\nb=1')).equal({ a: '', b: 1 }) + }) + + test('in-sections', () => { + const jn = makeWithNumbers() + + expect(jn('[s]\na=42\nb=text')) + .equal({ s: { a: 42, b: 'text' } }) + + expect(jn('[s]\na=1\n[t]\nb=2')) + .equal({ s: { a: 1 }, t: { b: 2 } }) + }) + + test('arrays', () => { + const jn = makeWithNumbers() + + expect(jn('a[]=1\na[]=2\na[]=hello')) + .equal({ a: [1, 2, 'hello'] }) + }) + + test('default-numbers-are-strings', () => { + // Without number.lex, all values are strings + const j = Jsonic.make().use(Ini) + + expect(j('a=1')).equal({ a: '1' }) + expect(j('a=2.5')).equal({ a: '2.5' }) + expect(j('a=-3')).equal({ a: '-3' }) + expect(j('a=0xFF')).equal({ a: '0xFF' }) + }) +}) diff --git a/test/quick.js b/test/quick.js deleted file mode 100644 index ac9c4c8..0000000 --- a/test/quick.js +++ /dev/null @@ -1,106 +0,0 @@ -const ini = require('ini') - -const { Jsonic } = require('@jsonic/jsonic-next') -const { Debug } = require('@jsonic/jsonic-next/debug') - -// console.log(Debug) - -const { Ini } = require('..') - -const j = Jsonic.make().use(Debug, { trace: true }).use(Ini, {}) - -console.dir( - j(` -a=1 - -`), -) - -// console.dir(j(` -// ; comment -// e[]=11 -// e[]=22 -// a=1 -// b=2 -// c c=3 -// [q] -// d=4 -// `)) - -// console.dir(j(` -// a.a=A -// s="\\n" -// q="'Q'" -// qq='Q' -// "[]"='[]' -// `)) - -// console.dir(j(` -// a0=0 -// a[]=1 -// a[]=2 -// a=3 -// a1=33 -// b[]=11 -// c=44 -// [q] -// w[]=55 -// w[]=66 -// `)) - -// console.dir(j(` -// [A] -// [B] -// [C] -// `)) - -// console.dir(j(` -// t=true -// [a.b] -// c=1 -// d=[1,2] -// e=]1,2[ -// `)) - -/* -console.dir(j(` -; comment -a = 1 -b = x -c = y y - - -[d] -e = 2 - -[f] -g = '1' - -[f1] -[f2] - - -[h.i] -#j = [3,4] -k = true - -[l.m.n.o] -p = "P" -q = {x:5} -u = v = 5 -w = "{y:{z:6}}" -aa = 7 - -`), { depth: null }) - - - -console.log(ini.decode(` -a = "{x:1}" -b = '{"x":1}' -aa = "1" -bb = '1' -`)) -*/ - -console.log(ini.decode(`=1`)) diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000..e6024df --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "module": "nodenext", + "noEmitOnError": true, + "outDir": "../dist-test", + "rootDir": ".", + "resolveJsonModule": true, + "sourceMap": true, + "strict": true, + "target": "ES2021" + } +}