Compare commits

...

76 commits
v0.0.8 ... main

Author SHA1 Message Date
c1b24a1d5f feat: descriptive props on the test page 2025-03-05 22:40:18 +01:00
6494a3bbed chore: update peer dep 2025-03-05 22:38:24 +01:00
08bcc85c93 chore: update repo info 2025-02-09 15:01:16 +01:00
62862b560f chore: add auto mirror to github build script 2025-02-09 14:59:12 +01:00
4c4730e812 0.4.3 2024-11-23 12:25:34 +01:00
439248e2e5 0.4.2 2024-11-23 12:24:43 +01:00
1565eb5a58 chore(deps): update astro and devalue 2024-11-23 12:24:27 +01:00
f01a3d8eea chore: update package repo & homepage 2024-08-03 11:43:45 +02:00
9512de21b5 chore: update readme w/ badges 2024-08-03 11:41:20 +02:00
b33251008f 0.4.1 2024-07-27 08:38:50 +02:00
5098a15bca chore: update deps 2024-07-27 08:38:43 +02:00
aa994c4793 chore: remove indentation in readme script examples 2024-04-10 16:39:11 +02:00
Ayo
3600df5279 0.4.0 2023-12-07 15:38:49 +01:00
Ayo
9107b00a6f chore: support Astro 4 2023-12-07 15:38:35 +01:00
Ayo
6be7424e07 0.3.9 2023-09-23 11:12:17 +02:00
Ayo
0c636da9c6 chore: udpate readme tldr 2023-09-23 11:12:10 +02:00
Ayo
5f3f504d35 0.3.8 2023-09-23 11:03:20 +02:00
Ayo
a4700f1f6a chore: update readme with tl;dr 2023-09-23 11:03:10 +02:00
Ayo
acef5b6ba6 0.3.7 2023-09-19 17:33:19 +02:00
Ayo
988fc65530 chore: update publish scripts 2023-09-19 17:33:10 +02:00
Ayo
7bc4956f91 0.3.6 2023-09-19 17:31:20 +02:00
Ayo
17ea41c47b chore: update publish scripts 2023-09-19 17:31:14 +02:00
Ayo
41af5c530b chore: update peerDeps support 2023-09-19 17:29:57 +02:00
Ayo
424e9e4494 0.3.5 2023-09-06 17:14:30 +02:00
Ayo
41e02ccf96 docs: add example of serialize once, access everywhere 2023-09-06 17:13:29 +02:00
Ayo
71a0e27c61 feat: add helpful error message when data is unserializable 2023-08-05 04:48:35 +02:00
Ayo
632e433add chore: remove long example 2023-07-28 10:29:10 +02:00
Ayo
4b29419ed3 chore: update package info 2023-07-28 10:26:31 +02:00
Ayo
e66111e5b9 0.3.4 2023-07-22 18:58:44 +02:00
Ayo
35e2620939 feat: put lib name in err messages 2023-07-22 18:58:19 +02:00
Ayo
cc26eb6bb5 0.3.3 2023-07-21 10:48:56 +02:00
Ayo
1956bee325 refactor: use devalue as devDep in test page 2023-07-21 10:48:43 +02:00
Ayo
08dde913c5 chore: add todo comment 2023-07-21 10:41:27 +02:00
Ayo Ayco
6f86eb4495 0.3.2 2023-07-18 23:36:00 +02:00
Ayo Ayco
94664e8c49 chore: update readme, code clean up 2023-07-18 23:35:51 +02:00
Ayo Ayco
3acc849f70 0.3.1 2023-07-18 22:23:33 +02:00
Ayo Ayco
ef8c086972 feat: query only JSON scripts 2023-07-18 22:23:21 +02:00
Ayo Ayco
1d7def5ea7 0.3.0 2023-07-18 22:08:15 +02:00
Ayo Ayco
2eb5b89124 feat: initial registry class 2023-07-18 22:08:01 +02:00
Ayo Ayco
c58b3aa152 feat: support custom serializer/parser
fixes #5
2023-07-18 22:07:45 +02:00
Ayo
7c82df8979 chore: update readme 2023-07-18 15:33:22 +02:00
Ayo
42b79466c5 0.2.2 2023-07-17 17:48:47 +02:00
Ayo
8191bb2bed chore: add more documentation 2023-07-17 17:48:00 +02:00
Ayo
41452dabbc 0.2.1 2023-07-17 17:18:03 +02:00
Ayo
f9a8cd9b01 feat: use textContent prop 2023-07-17 17:17:47 +02:00
Ayo
c540aff352 0.2.0 2023-07-17 17:09:33 +02:00
Ayo
0339b7c262 feat: use JSON script for serializing 2023-07-17 17:09:20 +02:00
Ayo
f584ba3b7e 0.1.9 2023-07-17 10:56:20 +02:00
Ayo
fd13caaffc chore: update readme 2023-07-17 10:56:06 +02:00
Ayo
ee04c9ab58 0.1.8 2023-07-17 10:50:59 +02:00
Ayo
3112df437f chore: update readme 2023-07-17 10:50:52 +02:00
Ayo
29ad406bdf 0.1.7 2023-07-17 10:47:52 +02:00
Ayo
3cf33188d1 0.1.6 2023-07-17 10:46:55 +02:00
Ayo
43b424332c chore: update readme 2023-07-17 10:46:49 +02:00
Ayo
4cd758b42e 0.1.5 2023-07-17 10:40:56 +02:00
Ayo
351c5e5b91 chore: update readme 2023-07-17 10:40:49 +02:00
Ayo
49ef3be87a chore: update readme 2023-07-17 10:39:56 +02:00
Ayo
abe1e9d2d2 refactor: clean code 2023-07-17 07:52:52 +02:00
Ayo
7989fe90c8 0.1.4 2023-07-16 22:12:39 +02:00
Ayo
3d42fd06b7 chore: update readme 2023-07-16 22:12:32 +02:00
Ayo
596c6325ce 0.1.3 2023-07-16 22:11:34 +02:00
Ayo
32229f993a chore: add example for passing all Astro.props 2023-07-16 22:11:18 +02:00
Ayo
1804904dd3 0.1.2 2023-07-16 21:56:14 +02:00
Ayo
ffd430a59a chore: update readme 2023-07-16 21:56:07 +02:00
Ayo
84c2161e4d 0.1.1 2023-07-16 21:53:49 +02:00
Ayo
fb5d905a75 chore: update readme 2023-07-16 21:53:43 +02:00
Ayo
6372fd5100 0.1.0 2023-07-16 21:52:16 +02:00
Ayo
57c47832fd refactor: use Serialize / deserialize as names 2023-07-16 21:51:55 +02:00
Ayo
9ccd6eb132 chore: add documentation/comments to props 2023-07-16 21:40:43 +02:00
Ayo
7b72595909 feat: error message when resume() did not find data 2023-07-16 21:32:03 +02:00
Ayo
0c9d64c28c feat: type for supported data (only primitives) 2023-07-16 21:31:44 +02:00
Ayo
0eb4f3c636 chore: add example for type safety 2023-07-16 20:50:44 +02:00
Ayo
b0dd9791a9 0.0.9 2023-07-16 20:20:04 +02:00
Ayo
2440646418 0.0.8 2023-07-16 20:19:22 +02:00
Ayo Ayco
fb93357cf7 0.0.8 2023-07-16 20:18:00 +02:00
Ayo
941d8da1ad feat: implement generic type or any 2023-07-16 20:14:47 +02:00
14 changed files with 3701 additions and 5667 deletions

11
.build.yml Normal file
View file

@ -0,0 +1,11 @@
image: alpine/edge
secrets:
- bbfcb6dc-7c4a-42ee-a11a-022f0339a133
environment:
REPO: astro-resume
GH_USER: ayoayco
tasks:
- push-mirror: |
cd ~/"${REPO}"
git config --global credential.helper store
git push --mirror "https://github.com/${GH_USER}/${REPO}"

193
README.md
View file

@ -1,11 +1,20 @@
> [!NOTE]
> This project moved to [SourceHut](https://git.sr.ht/~ayoayco/astro-resume).
# Astro Resume # Astro Resume
[![Package information: NPM version](https://img.shields.io/npm/v/@ayco/astro-resume)](https://www.npmjs.com/package/@ayco/astro-resume)
[![Package information: NPM license](https://img.shields.io/npm/l/@ayco/astro-resume)](https://www.npmjs.com/package/@ayco/astro-resume)
[![Package information: NPM downloads](https://img.shields.io/npm/dt/@ayco/astro-resume)](https://www.npmjs.com/package/@ayco/astro-resume)
Utilities for serializing data from server for use in the client. Utilities for serializing data from server for use in the client.
1. `Resumable` Astro component takes `id` and `data` 1. `Serialize` - Astro component that takes `id` and `data`
1. `resume(id: string): Object` function for use int he client takes an `id` string and returns the `data` as Object 1. `deserialize()` - a function for use in the client that takes an `id` string and returns the `data` object
## Intallation ## Install via npm
On your [Astro](https://astro.build) project:
``` ```
npm i @ayco/astro-resume npm i @ayco/astro-resume
@ -13,26 +22,186 @@ npm i @ayco/astro-resume
## Usage ## Usage
Serializing and deserializing basic primitive data
```astro ```astro
--- ---
import Resumable from "@ayco/astro-resume"; import Serialize from "@ayco/astro-resume";
const data = { const data = {
hello: 'world' hello: 'world',
} }
--- ---
<Resumable id="astro-obj" data={data} /> <Serialize id="my-data" data={data} />
<script> <script>
import {resume} from '@ayco/astro-resume'; import {deserialize} from '@ayco/astro-resume';
console.log( const data = deserialize('my-data');
resume('astro-obj') console.log(data) // {hello: 'world'}
)
</script> </script>
``` ```
## Found issues? ## Type Safety
Send a plain text email to [~ayoayco/astro-resume@todo.sr.ht](mailto:~ayoayco/astro-resume@todo.sr.ht) or report via [SourceHut](https://todo.sr.ht/~ayoayco/astro-resume) You can define a type for the data and use it in the client script.
```astro
---
import Serialize from "@ayco/astro-resume";
const data = {
hello: 'world',
isOkay: true
}
// define the type of data to be serialized
export type Data = typeof data;
---
<Serialize id="my-data" data={data} />
<script>
import {deserialize} from '@ayco/astro-resume';
/**
* reuse the type in the client
* assuming this component's name is `ThisComponent.astro`
*/
import type {Data} from './ThisComponent.astro';
const data = deserialize<Data>('my-data');
console.log(data) // {hello: 'world', isOkay: true}
</script>
```
## Passing all Astro.props to client
If you need to make all the component props to the client script:
```astro
---
import Serialize from "@ayco/astro-resume";
export interface Props {
hello: string;
isOkay: boolean;
}
---
<Serialize id="preferences" data={{...Astro.props}} />
<script>
import {deserialize} from '@ayco/astro-resume';
import type {Props} from './ThisComponent.astro';
const {hello, isOkay} = deserialize<Props>('preferences');
console.log(hello, isOkay);
</script>
```
## Serialize server data once, access everywhere
If you have shared data that needs to be initialized from the server and accessed in several places on the client-side, you can use `Serialize` once and `deserialize` in any number of Astro components as long as they are in the same page.
In this example, an appConfig object is built and serialized in index.astro and accessed in child Astro components.
In index.astro:
```astro
import Serialize from "@ayco/astro-resume";
const appConfig = {
someClientSideKey: '1234hello',
}
export type AppConfig = typeof appConfig;
---
<Serialize id="app-config" data={appConfig} />
<Child />
```
In Child.astro:
```astro
<h1>I'm a child. I have access to the appConfig in index!</h1>
<GrandChild />
<script>
import {deserialize} from '@ayco/astro-resume';
import type {AppConfig} from '..pages/index.astro';
const data = deserialize<AppConfig>('app-config');
// ... do something with the app config
</script>
```
In GrandChild.astro:
```astro
<h1>I'm a grand child. I also have access to the appConfig in index!</h1>
<script>
import {deserialize} from '@ayco/astro-resume';
import type {AppConfig} from '..pages/index.astro';
const data = deserialize<AppConfig>('app-config');
// ... do something with the app config
</script>
```
## Using a custom serializer and parser
You can bring your own custom serializer/parser if you want to opt for more complex data handling.
Here's an example of serializing data that `JSON.stringify` cannot (e.g., Date or BigInt) using Rich Harris' [`devalue`](https://github.com/Rich-Harris/devalue):
```astro
---
import {stringify} from 'devalue';
import Serialize from "@ayco/astro-resume";
const data = {
now: new Date(),
age: BigInt('3218378192378')
}
export type Data = typeof data;
---
<Serialize data={data} id="my-data" use={stringify} />
<script>
import {parse} from 'devalue';
import {deserialize} from '@ayco/astro-resume';
import type {Data} from './index.astro';
const {age, now} = deserialize<Data>('my-data', parse);
console.log(typeof age); // 'bigint'
console.log(now instanceof Date); // true
</script>
```
## Errors & Warning in `deserialize()`
The `deserialize()` function may give you the following:
1. **ERR: No match found** - there are no `JSON` scripts with the given ID
1. **WARNING: Multiple matches for <id>** - there were multiple `JSON` scripts found with the same ID
## 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.
The `Serialize` component will write the data as JSON wrapped in a `<script type="application/json">` element to hold the string.
The `deserialize()` function can then parse the value string for use in your client script.
There is also a pattern [given in the Astro docs](https://docs.astro.build/en/guides/client-side-scripts/#pass-frontmatter-variables-to-scripts) to use a Custom Element that takes a `data-` prop which properly protects the scope of your component. That is a good pattern to follow for complex applications that don't use UI frameworks.
## Trade-Off
Some other frameworks themselves will manage serialized information and the IDs for you, but we don't have access to this in Astro as we are not really shipping a framework to the browser.
That's nice and ideal (in my opinion), as we are aware of how the HTML is formed and what we are shipping to our users. The trade off is that we do have to manage things ourselves.
You have to manage the IDs (i.e., make sure they are unique) and understand that the `deserialize()` function will crawl the whole document incurring a minimal performance cost depending on how big your HTML is.
## Road Map
See the [TODO tracker](https://todo.sr.ht/~ayoayco/astro-resume) for planned work items.
## Reporting Issues
To report issues or request features, send a plain text email to [~ayoayco/astro-resume@todo.sr.ht](mailto:~ayoayco/astro-resume@todo.sr.ht) or file a ticket via [SourceHut](https://todo.sr.ht/~ayoayco/astro-resume)

View file

@ -1,4 +1,6 @@
import { defineConfig } from 'astro/config'; import { defineConfig } from 'astro/config';
// https://astro.build/config // https://astro.build/config
export default defineConfig({}); export default defineConfig({
output: "server",
});

View file

@ -1,5 +1,5 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
import Resumable from './src/Resumable.astro'; import Serialize from './src/Serialize.astro';
export default Resumable; export default Serialize;
export * from './src/resume'; export * from './src/deserialize';

5620
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,13 @@
{ {
"name": "@ayco/astro-resume", "name": "@ayco/astro-resume",
"author": "Ayo Ayco", "author": "Ayo Ayco",
"homepage": "https://sr.ht/~ayoayco/astro-resume", "homepage": "https://ayco.io/sh/astro-resume",
"repository": {
"type": "git",
"url": "https://git.sr.ht/~ayoayco/astro-resume"
},
"type": "module", "type": "module",
"version": "0.0.7", "version": "0.4.3",
"keywords": [ "keywords": [
"astro-component", "astro-component",
"css", "css",
@ -14,8 +18,8 @@
".": "./index.ts" ".": "./index.ts"
}, },
"files": [ "files": [
"src/Resumable.astro", "src/Serialize.astro",
"src/resume.ts", "src/deserialize.ts",
"index.ts" "index.ts"
], ],
"scripts": { "scripts": {
@ -23,9 +27,14 @@
"start": "astro dev", "start": "astro dev",
"build": "astro build", "build": "astro build",
"preview": "astro preview", "preview": "astro preview",
"astro": "astro" "astro": "astro",
"publish:patch": "npm version patch && npm publish --access public",
"publish:minor": "npm version minor && npm publish --access public"
}, },
"dependencies": { "devDependencies": {
"astro": "^2.8.3" "devalue": "^5.1.1"
},
"peerDependencies": {
"astro": "^5"
} }
} }

3370
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,9 +0,0 @@
---
export interface Props {
id: string;
data: Object;
}
const {id, data} = Astro.props;
---
<textarea hidden id={id}>{JSON.stringify(data)}</textarea>

36
src/Serialize.astro Normal file
View file

@ -0,0 +1,36 @@
---
// type Primitive = string | number | boolean | null | undefined;
export interface Props {
/**
* The id that the client script will pass to the `deserialize()` function
*/
id: string;
/**
* The data to be serialized and accessed in the client script with `deserialize()`
*/
data: Record<string, any>;
/**
* Custom serializer to be used
* @param data
*/
use?: (data: Record<string, any>) => string;
}
const {id, data, use} = Astro.props;
let serializedData = '{}'
try {
serializedData = use ? use(data) : JSON.stringify(data);
} catch(err) {
/**
* ERR: data is unserializable
* - You might need a custom serializer/parser for complex data
* - Usage examples in 👉 https://git.sr.ht/~ayoayco/astro-resume#astro-resume
*/
throw Error(`astro-resume ERR: Data unserializable
- You might need a custom serializer/parser for complex data
- Usage examples in 👉 https://git.sr.ht/~ayoayco/astro-resume#astro-resume
`, err)
}
---
<script type="application/json" id={id} set:html={serializedData}></script>

31
src/deserialize.ts Normal file
View file

@ -0,0 +1,31 @@
/**
* 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
**/
export function deserialize<T = any>(id: string, parser?: (serialized: string)=>any): T {
const elements = document.querySelectorAll<HTMLScriptElement>(`script#${id}[type="application/json"]`);
if (elements?.length > 0) {
if (elements?.length > 1)
console.warn(`astro-resume WARN: Multiple matches for "${id}". The function will parse the first one.`)
const element = elements[0];
if (element?.textContent)
return parser
? parser(element.textContent)
: JSON.parse(element.textContent)
}
throw Error(`astro-resume 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
- "${id}" is the "id" of the Serialize component
See examples: https://sr.ht/~ayoayco/astro-resume/#usage
Stack trace: `)
}

1
src/env.d.ts vendored
View file

@ -1 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" /> /// <reference types="astro/client" />

View file

@ -1,19 +1,44 @@
--- ---
import Resumable from '../Resumable.astro'; import Serialize from "../Serialize.astro";
import { stringify } from "devalue";
const data = { const data = {
hello: 'world' nameStr: "John Doe",
} isOkayBool: true,
moodNull: null,
nowDate: new Date(),
ageBigInt: BigInt("3218378192378"),
};
export type Data = typeof data;
--- ---
<Resumable id="astro-obj" data={data} />
<div id="render-here"></div> <div id="render-here"></div>
<Serialize data={data} id="my-data" use={stringify} />
<script> <script>
import {resume} from '../resume'; import { deserialize } from "../deserialize";
console.log(resume('astro-obj')); import { parse } from "devalue";
const renderDiv = document.querySelector('#render-here'); import type { Data } from "./index.astro";
if (renderDiv) {
renderDiv.innerHTML = JSON.stringify(resume('astro-obj')) const data = deserialize<Data>("my-data", parse);
} console.log(data);
</script>
Object.keys(data).forEach((key) =>
console.log(key, data[key], typeof data[key])
);
// render table to render-here
const table = document.createElement("table");
const tbody = document.createElement("tbody");
table.appendChild(tbody);
Object.keys(data).forEach((key) => {
const tr = document.createElement("tr");
const tdKey = document.createElement("td");
const tdValue = document.createElement("td");
tdKey.textContent = key;
tdValue.textContent = data[key];
tr.appendChild(tdKey);
tr.appendChild(tdValue);
tbody.appendChild(tr);
});
document.getElementById("render-here").appendChild(table);
</script>

12
src/registry.ts Normal file
View file

@ -0,0 +1,12 @@
export class SerializedRegistry {
private _registeredIds: string[]
constructor() {}
get registeredIds() {
return this._registeredIds;
}
public register(id: string) {
if (this._registeredIds.includes(id))
throw Error(`${id} is already used`)
this._registeredIds.push(id);
}
}

View file

@ -1,3 +0,0 @@
export function resume(id: string): Object {
return JSON.parse(document.querySelector<HTMLTextAreaElement>(`#${id}`)?.value ?? '')
}