diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8712e3..2edff8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,12 +18,12 @@ jobs: strategy: fail-fast: false matrix: - node-version: [16] + node-version: [16, 18, 20] os: [ubuntu-latest] steps: - run: git config --global core.autocrlf false - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v2.2.2 + - uses: pnpm/action-setup@v2 - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} diff --git a/.gitignore b/.gitignore index 4053120..2c5a5ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .DS_Store -node_modules -*.d.ts -dist \ No newline at end of file +/node_modules +/types \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 31a9581..4724a8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # devalue changelog +## 4.3.2 + +- Better type declarations ([#66](https://github.com/Rich-Harris/devalue/pull/66)) + +## 4.3.1 + +- Faster ([#65](https://github.com/Rich-Harris/devalue/pull/65)) + ## 4.3.0 - Support custom types ([#58](https://github.com/Rich-Harris/devalue/pull/58)) diff --git a/package.json b/package.json index 644fea6..cf3aeb1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "devalue", "description": "Gets the job done when JSON.stringify can't", - "version": "4.3.0", + "version": "4.3.2", "repository": "Rich-Harris/devalue", "exports": { ".": { @@ -17,16 +17,17 @@ ], "types": "./types/index.d.ts", "devDependencies": { + "dts-buddy": "^0.0.4", "publint": "^0.1.7", "typescript": "^3.1.3", "uvu": "^0.5.6" }, "scripts": { - "build": "tsc", + "build": "dts-buddy", "test": "uvu test", "prepublishOnly": "npm test && publint && npm run build" }, "license": "MIT", "type": "module", - "packageManager": "pnpm@7.9.5" -} + "packageManager": "pnpm@8.5.1" +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70fc615..07e4699 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,42 +1,102 @@ -lockfileVersion: 5.4 - -specifiers: - publint: ^0.1.7 - typescript: ^3.1.3 - uvu: ^0.5.6 +lockfileVersion: '6.0' devDependencies: - publint: 0.1.7 - typescript: 3.9.10 - uvu: 0.5.6 + dts-buddy: + specifier: ^0.0.4 + version: 0.0.4 + publint: + specifier: ^0.1.7 + version: 0.1.7 + typescript: + specifier: ^3.1.3 + version: 3.9.10 + uvu: + specifier: ^0.5.6 + version: 0.5.6 packages: - /balanced-match/1.0.2: + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.18 + dev: true + + /@jridgewell/resolve-uri@3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/source-map@0.3.3: + resolution: {integrity: sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 + dev: true + + /@jridgewell/sourcemap-codec@1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.18: + resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /brace-expansion/2.0.1: + /brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 dev: true - /dequal/2.0.3: + /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} dev: true - /diff/5.1.0: + /diff@5.1.0: resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} engines: {node: '>=0.3.1'} dev: true - /fs.realpath/1.0.0: + /dts-buddy@0.0.4: + resolution: {integrity: sha512-3joGIpq3jZ3LMdeUBNFh4mh7pg9016699VpQIBow9nZ6S35/DFwVKpoaJSg534mBAjGU1w7tPHGb0TOLG4eqgg==} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.3 + '@jridgewell/sourcemap-codec': 1.4.15 + globrex: 0.1.2 + kleur: 4.1.5 + locate-character: 2.0.5 + magic-string: 0.30.0 + sade: 1.8.1 + tiny-glob: 0.2.9 + typescript: 5.0.4 + dev: true + + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true - /glob/8.0.3: + /glob@8.0.3: resolution: {integrity: sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==} engines: {node: '>=12'} dependencies: @@ -47,54 +107,73 @@ packages: once: 1.4.0 dev: true - /ignore-walk/5.0.1: + /globalyzer@0.1.0: + resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + dev: true + + /globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: true + + /ignore-walk@5.0.1: resolution: {integrity: sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: minimatch: 5.1.2 dev: true - /inflight/1.0.6: + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: once: 1.4.0 wrappy: 1.0.2 dev: true - /inherits/2.0.4: + /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true - /kleur/4.1.5: + /kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} dev: true - /minimatch/5.1.2: + /locate-character@2.0.5: + resolution: {integrity: sha512-n2GmejDXtOPBAZdIiEFy5dJ5N38xBCXLNOtw2WpB9kGh6pnrEuKlwYI+Tkpofc4wDtVXHtoAOJaMRlYG/oYaxg==} + dev: true + + /magic-string@0.30.0: + resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /minimatch@5.1.2: resolution: {integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==} engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 dev: true - /mri/1.2.0: + /mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} dev: true - /npm-bundled/2.0.1: + /npm-bundled@2.0.1: resolution: {integrity: sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: npm-normalize-package-bin: 2.0.0 dev: true - /npm-normalize-package-bin/2.0.0: + /npm-normalize-package-bin@2.0.0: resolution: {integrity: sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dev: true - /npm-packlist/5.1.3: + /npm-packlist@5.1.3: resolution: {integrity: sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} hasBin: true @@ -105,17 +184,17 @@ packages: npm-normalize-package-bin: 2.0.0 dev: true - /once/1.4.0: + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 dev: true - /picocolors/1.0.0: + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true - /publint/0.1.7: + /publint@0.1.7: resolution: {integrity: sha512-/YqYkp7xP3IeOPGgcpHK4wWkZaZgf3KcQrvqsu4Wi2Rws/NTYeFm64OpgYSPLXXz7y9BjgU0q22F8rSGel9/Ig==} engines: {node: '>=16'} hasBin: true @@ -125,20 +204,33 @@ packages: sade: 1.8.1 dev: true - /sade/1.8.1: + /sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} dependencies: mri: 1.2.0 dev: true - /typescript/3.9.10: + /tiny-glob@0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + dependencies: + globalyzer: 0.1.0 + globrex: 0.1.2 + dev: true + + /typescript@3.9.10: resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} engines: {node: '>=4.2.0'} hasBin: true dev: true - /uvu/0.5.6: + /typescript@5.0.4: + resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} + engines: {node: '>=12.20'} + hasBin: true + dev: true + + /uvu@0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} engines: {node: '>=8'} hasBin: true @@ -149,6 +241,6 @@ packages: sade: 1.8.1 dev: true - /wrappy/1.0.2: + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true diff --git a/src/uneval.js b/src/uneval.js index 79f35a3..21a98a4 100644 --- a/src/uneval.js +++ b/src/uneval.js @@ -8,7 +8,7 @@ import { } from './utils.js'; const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$'; -const unsafe_chars = /[<>\b\f\n\r\t\0\u2028\u2029]/g; +const unsafe_chars = /[<\b\f\n\r\t\0\u2028\u2029]/g; const reserved = /^(?:do|if|in|for|int|let|new|try|var|byte|case|char|else|enum|goto|long|this|void|with|await|break|catch|class|const|final|float|short|super|throw|while|yield|delete|double|export|import|native|return|switch|throws|typeof|boolean|default|extends|finally|package|private|abstract|continue|debugger|function|volatile|interface|protected|transient|implements|instanceof|synchronized)$/; diff --git a/src/utils.js b/src/utils.js index 6bf9674..5dea3ef 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,15 +1,12 @@ /** @type {Record} */ export const escaped = { '<': '\\u003C', - '>': '\\u003E', - '/': '\\u002F', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t', - '\0': '\\u0000', '\u2028': '\\u2028', '\u2029': '\\u2029' }; @@ -31,7 +28,9 @@ export function is_primitive(thing) { return Object(thing) !== thing; } -const object_proto_names = Object.getOwnPropertyNames(Object.prototype) +const object_proto_names = /* @__PURE__ */ Object.getOwnPropertyNames( + Object.prototype +) .sort() .join('\0'); @@ -51,35 +50,50 @@ export function get_type(thing) { return Object.prototype.toString.call(thing).slice(8, -1); } +/** @param {string} char */ +function get_escaped_char(char) { + switch (char) { + case '"': + return '\\"'; + case '<': + return '\\u003C'; + case '\\': + return '\\\\'; + case '\n': + return '\\n'; + case '\r': + return '\\r'; + case '\t': + return '\\t'; + case '\b': + return '\\b'; + case '\f': + return '\\f'; + case '\u2028': + return '\\u2028'; + case '\u2029': + return '\\u2029'; + default: + return char < ' ' + ? `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}` + : ''; + } +} + /** @param {string} str */ export function stringify_string(str) { - let result = '"'; - - for (let i = 0; i < str.length; i += 1) { - const char = str.charAt(i); - const code = char.charCodeAt(0); - - if (char === '"') { - result += '\\"'; - } else if (char in escaped) { - result += escaped[char]; - } else if (code <= 0x001F) { - result += `\\u${code.toString(16).toUpperCase().padStart(4, "0")}` - } else if (code >= 0xd800 && code <= 0xdfff) { - const next = str.charCodeAt(i + 1); + let result = ''; + let last_pos = 0; + const len = str.length; - // If this is the beginning of a [high, low] surrogate pair, - // add the next two characters, otherwise escape - if (code <= 0xdbff && next >= 0xdc00 && next <= 0xdfff) { - result += char + str[++i]; - } else { - result += `\\u${code.toString(16).toUpperCase()}`; - } - } else { - result += char; + for (let i = 0; i < len; i += 1) { + const char = str[i]; + const replacement = get_escaped_char(char); + if (replacement) { + result += str.slice(last_pos, i) + replacement; + last_pos = i + 1; } } - result += '"'; - return result; + return `"${last_pos === 0 ? str : result + str.slice(last_pos)}"`; } diff --git a/test/test.js b/test/test.js index ecc786e..1146d7a 100644 --- a/test/test.js +++ b/test/test.js @@ -9,6 +9,8 @@ class Custom { } } +const node_version = +process.versions.node.split('.')[0]; + const fixtures = { basics: [ { @@ -167,26 +169,26 @@ const fixtures = { { name: 'lone low surrogate', value: 'a\uDC00b', - js: '"a\\uDC00b"', - json: '["a\\uDC00b"]' + js: '"a\uDC00b"', + json: '["a\uDC00b"]' }, { name: 'lone high surrogate', value: 'a\uD800b', - js: '"a\\uD800b"', - json: '["a\\uD800b"]' + js: '"a\uD800b"', + json: '["a\uD800b"]' }, { name: 'two low surrogates', value: 'a\uDC00\uDC00b', - js: '"a\\uDC00\\uDC00b"', - json: '["a\\uDC00\\uDC00b"]' + js: '"a\uDC00\uDC00b"', + json: '["a\uDC00\uDC00b"]' }, { name: 'two high surrogates', value: 'a\uD800\uD800b', - js: '"a\\uD800\\uD800b"', - json: '["a\\uD800\\uD800b"]' + js: '"a\uD800\uD800b"', + json: '["a\uD800\uD800b"]' }, { name: 'surrogate pair', @@ -197,8 +199,8 @@ const fixtures = { { name: 'surrogate pair in wrong order', value: 'a\uDC00\uD800b', - js: '"a\\uDC00\\uD800b"', - json: '["a\\uDC00\\uD800b"]' + js: '"a\uDC00\uD800b"', + json: '["a\uDC00\uD800b"]' }, { name: 'nul', @@ -215,8 +217,8 @@ const fixtures = { { name: 'control character extremum', value: '\u001F', - js: '"\\u001F"', - json: '["\\u001F"]' + js: '"\\u001f"', + json: '["\\u001f"]' }, { name: 'backslash', @@ -342,20 +344,20 @@ const fixtures = { { name: 'Dangerous string', value: `