diff --git a/README.md b/README.md index 6d90142..32dda43 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,38 @@ export interface Props { ``` -## What's happening here? +### Using a custom serializer and parser + +You can bring your own serializer/parser. For serializing data that `JSON.parse` cannot parse (e.g., Date or BigInt), here's an example of using Rich Harris' `devalue` + +```astro +--- +import {stringify} from 'devalue'; +import Serialize from "../Serialize.astro"; +const data = { + name: 'John Doe', + isOkay: true, + mood: null, + now: new Date(), + age: BigInt('3218378192378') +} +export type Data = typeof data; +--- + + + + +``` + +## About This is a quick and easy pattern to embed serialized information into your HTML and make it available in the client-side script with type safety. diff --git a/astro.config.mjs b/astro.config.mjs index 882e651..97e31f6 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,4 +1,11 @@ import { defineConfig } from 'astro/config'; +import node from "@astrojs/node"; + // https://astro.build/config -export default defineConfig({}); +export default defineConfig({ + output: "server", + adapter: node({ + mode: "standalone" + }) +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b94d86e..00e887a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,9 @@ "version": "0.2.2", "license": "MIT", "dependencies": { - "astro": "^2.8.3" + "@astrojs/node": "^5.3.0", + "astro": "^2.8.4", + "devalue": "^4.3.2" } }, "node_modules/@ampproject/remapping": { @@ -85,6 +87,19 @@ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==" }, + "node_modules/@astrojs/node": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-5.3.0.tgz", + "integrity": "sha512-q7gJEPSZzC4FIRNmq3ks6mw0Jby4irXTfRhbOPOS4HRmbu4FDANnRqnZBOGe96cfBQTgDC3/FhQccDQtx+n4pg==", + "dependencies": { + "@astrojs/webapi": "^2.2.0", + "send": "^0.18.0", + "server-destroy": "^1.0.1" + }, + "peerDependencies": { + "astro": "^2.7.0" + } + }, "node_modules/@astrojs/prism": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-2.1.2.tgz", @@ -1139,9 +1154,9 @@ } }, "node_modules/astro": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/astro/-/astro-2.8.3.tgz", - "integrity": "sha512-UopGl3WubMnl15TWlR6PTCcH54TooZ+JoAloErfoXQtz4PAvmEEqEVc7tHoJ45iC7rhKgzILsvDhqzitBeRihw==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/astro/-/astro-2.8.4.tgz", + "integrity": "sha512-XTfMN6MszrkePcaLt19eQl5BA3Mg8Jqvb7wDWBYoQAJQwU0YE3BBbAwFgWosA1rBv4wClWIne8CKq5iBDklrOQ==", "dependencies": { "@astrojs/compiler": "^1.5.3", "@astrojs/internal-helpers": "^0.1.1", @@ -1792,6 +1807,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -1800,6 +1823,15 @@ "node": ">=6" } }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/devalue": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz", @@ -1831,6 +1863,11 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, "node_modules/electron-to-chromium": { "version": "1.4.461", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.461.tgz", @@ -1850,6 +1887,14 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/es-module-lexer": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", @@ -1899,6 +1944,11 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -1924,6 +1974,14 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.0.tgz", "integrity": "sha512-s6ceX0NFiU/vKPiKvFdR83U1Zffu7upwZsGwpoqfg5rbbq1l50WQ5hCeIvM6E6oD4shUHCYMsiFPns4Jk0YfMQ==" }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -2028,6 +2086,14 @@ "pkg-dir": "^4.2.0" } }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -2277,6 +2343,21 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/human-signals": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", @@ -3564,6 +3645,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", @@ -3943,6 +4035,14 @@ } ] }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -4421,11 +4521,68 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/server-destroy": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==" }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4493,6 +4650,14 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stdin-discarder": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", @@ -4668,6 +4833,14 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", diff --git a/package.json b/package.json index fe5e23b..4122410 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ "astro": "astro" }, "dependencies": { - "astro": "^2.8.3" + "@astrojs/node": "^5.3.0", + "astro": "^2.8.4", + "devalue": "^4.3.2" } } diff --git a/src/Serialize.astro b/src/Serialize.astro index f137caa..e8ac4d3 100644 --- a/src/Serialize.astro +++ b/src/Serialize.astro @@ -1,5 +1,5 @@ --- -type Primitive = string | number | boolean | null | undefined; +// type Primitive = string | number | boolean | null | undefined; export interface Props { /** @@ -9,9 +9,20 @@ export interface Props { /** * The data to be serialized and accessed in the client script with `deserialize()` */ - data: Record; + data: Record; + /** + * Custom serializer to be used + * @param data + */ + use?: (data: Record) => string; +} +const {id, data, use} = Astro.props; +let serializedData = '{}' +try { + serializedData = use ? use(data) : JSON.stringify(data); +} catch(err) { + throw Error(err) } -const {id, data} = Astro.props; --- - \ No newline at end of file + \ No newline at end of file diff --git a/src/deserialize.ts b/src/deserialize.ts index 5abbc2b..8d1da46 100644 --- a/src/deserialize.ts +++ b/src/deserialize.ts @@ -1,6 +1,7 @@ /** * Function to find and deserialize JSON data from the HTML * @param id The id of the Serialize component, used to find the serialized data in the HTML + * @param parser Custom parser to be used * @returns The deserialized JSON data * @see Usage examples in 👉 https://git.sr.ht/~ayoayco/astro-resume#astro-resume * @example @@ -26,13 +27,23 @@ * * ``` **/ -export function deserialize(id: string): T { - const element = document.querySelector(`#${id}`); +export function deserialize(id: string, parser?: (serialized: string)=>any): T { + const elements = document.querySelectorAll(`#${id}`); - if (element?.textContent) - return JSON.parse(element.textContent) + if (elements?.length > 0) { + if (elements?.length > 1) + console.warn(`WARNING: Multiple matches for "${id}". There are ${elements?.length} matches found for ID: "${id}". The function will parse the first match found.`) + + const element = elements[0]; + + if (element?.textContent) + return parser + ? parser(element.textContent) + : JSON.parse(element.textContent) + } - throw Error(`The call deserialize('${id}') did not find any data. + throw Error(`ERR: No match found. + "deserialize('${id}')" did not find any data. Check that the following are correct: - The Serialize component is used with correct props - "data" prop is not undefined diff --git a/src/pages/index.astro b/src/pages/index.astro index 3dae2d4..d1e150c 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,25 +1,27 @@ --- +import { stringify } from 'devalue'; import Serialize from "../Serialize.astro"; const data = { name: 'John Doe', isOkay: true, - mood: null + mood: null, + now: new Date(), + age: BigInt('3218378192378') } export type Data = typeof data; --- - -
+