vite-plugin-vanjs 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,17 +1,31 @@
1
1
  ## vite-plugin-vanjs
2
2
 
3
+ [![Coverage Status](https://coveralls.io/repos/github/thednp/vite-plugin-vanjs/badge.svg)](https://coveralls.io/github/thednp/vite-plugin-vanjs)
4
+ [![ci](https://github.com/thednp/vite-plugin-vanjs/actions/workflows/ci.yml/badge.svg)](https://github.com/thednp/vite-plugin-vanjs/actions/workflows/ci.yml)
3
5
  [![NPM Version](https://img.shields.io/npm/v/vite-plugin-vanjs.svg)](https://www.npmjs.com/package/vite-plugin-vanjs)
4
6
  [![NPM Downloads](https://img.shields.io/npm/dm/vite-plugin-vanjs.svg)](http://npm-stat.com/charts.html?package=vite-plugin-vanjs)
5
7
  [![typescript version](https://img.shields.io/badge/typescript-5.6.2-brightgreen)](https://www.typescriptlang.org/)
6
- [![vanjs-core version](https://img.shields.io/badge/vanjs--core-1.5.2-brightgreen)](https://github.com/vanjs-org/van)
7
- [![mini-van-plate version](https://img.shields.io/badge/mini--van--plate-0.6.1-brightgreen)](https://github.com/vanjs-org/mini-van-plate)
8
- [![vite version](https://img.shields.io/badge/vite-6.0.6-brightgreen)](https://github.com/vitejs)
8
+ [![vanjs-core version](https://img.shields.io/badge/vanjs--core-1.5.3-brightgreen)](https://github.com/vanjs-org/van)
9
+ [![mini-van-plate version](https://img.shields.io/badge/mini--van--plate-0.6.3-brightgreen)](https://github.com/vanjs-org/mini-van-plate)
10
+ [![vitest version](https://img.shields.io/badge/vitest-3.0.4-brightgreen)](https://www.vitest.dev/)
11
+ [![vite version](https://img.shields.io/badge/vite-6.0.11-brightgreen)](https://vite.dev)
9
12
 
10
- A Vite plugin for VanJS to enable JSX transformation and simplify application development and enables you to create SSR applications or SSG (static) pages. It will automatically load the appropriate Van or VanX objects depending on the client/server environment with zero configuration needed. It uses the `mini-van-plate/shared` module to register the required objects in an isomorphic enviroment.
13
+ A mini meta-framework for [VanJS](https://vanjs.org/) developed around the awesome [Vite](https://vite.dev). The plugin comes with a set of modules to simplify your workflow:
14
+ * ***@vanjs/router*** - one of the most important part of an application, this one allows you to split code and lazy load components with ease, handles both Client Side Rendering (SSR) and Server Side Rendering (CSR) and makes it really easy to work with;
15
+ * ***@vanjs/meta*** - allows you to create metadata for your pages as well as load additional assets with ease;
16
+ * ***@vanjs/jsx*** - enables JSX transformation;
17
+ * ***@vanjs/setup*** - enables loading VanJS modules isomorphically;
18
+ * ***@vanjs/server*** - provides various tools for Server Side Rendering;
19
+ * ***@vanjs/client*** - provides various tools for Client Side Rendering.
11
20
 
12
- The plugin uses `van-ext` along with `mini-van-plate` so you can have everything ready from start. It also enables JSX transformation with minimal setup so you can write your code faster.
21
+ The plugin will automatically load the appropriate Van or VanX objects depending on the client/server environment with zero configuration needed. It uses the `mini-van-plate/shared` module to register the required objects in an isomorphic enviroment.
13
22
 
14
- The recommended starter templates for you are the [vite-starter-vanjs-ssr](https://github.com/thednp/vite-starter-vanjs-ssr) and [vite-starter-vanjs-ssr-jsx](https://github.com/thednp/vite-starter-vanjs-ssr-jsx) which already include this plugin.
23
+ ### Notes
24
+ * The plugin uses `van-ext` along with `mini-van-plate` so you can have everything ready from start.
25
+
26
+ * The recommended starter templates for you are the [vite-starter-vanjs-ssr](https://github.com/thednp/vite-starter-vanjs-ssr) and [vite-starter-vanjs-ssr-jsx](https://github.com/thednp/vite-starter-vanjs-ssr-jsx) which already include this plugin.
27
+
28
+ * In the near future we're going to have a `npm create-vanjs` CLI so stick around!
15
29
 
16
30
 
17
31
  ### Install
@@ -19,7 +33,7 @@ The recommended starter templates for you are the [vite-starter-vanjs-ssr](https
19
33
  1) Install the plugin:
20
34
 
21
35
  ```bash
22
- pnpm add vite-plugin-vanjs
36
+ pnpm install vite-plugin-vanjs
23
37
  ```
24
38
 
25
39
  ```bash
@@ -27,10 +41,18 @@ npm install vite-plugin-vanjs
27
41
  ```
28
42
 
29
43
  ```bash
30
- deno add npm:vite-plugin-vanjs
44
+ deno install npm:vite-plugin-vanjs
45
+ ```
46
+
47
+ ```bash
48
+ bun add vite-plugin-vanjs
31
49
  ```
32
50
 
33
- 2) To add typescript support, edit your `src/vite-env.d.ts` as follows:
51
+ ```bash
52
+ yarn add vite-plugin-vanjs
53
+ ```
54
+
55
+ 2) To add typescript support, edit your `src/vite-env.d.ts` (or any global types you have set in your app) as follows:
34
56
 
35
57
  ```ts
36
58
  /// <reference types="vite/client" />
@@ -54,7 +76,7 @@ export default defineConfig({
54
76
 
55
77
  **Example**:
56
78
 
57
- While the plugin will resolve the appropriate modules depending on the environment, for your convenience, you can also import the `@vanjs/van` and `@vanjs/vanX` virtual modules, so the plugin makes sure to load the right modules where needed.
79
+ While the plugin will resolve the appropriate modules automatically depending on the environment, for your convenience, you can also import the `@vanjs/van` and `@vanjs/vanX` virtual modules, so the plugin makes sure to load the right modules where needed.
58
80
 
59
81
  ```ts
60
82
  // my-component.ts
@@ -65,7 +87,7 @@ const MyComponent = () => {
65
87
  return van.div("Hello from VanJS!");
66
88
  };
67
89
  ```
68
- The plugin also exports `vanX`.
90
+ Using `vanX` in your code:
69
91
 
70
92
  ```ts
71
93
  // my-list.ts
@@ -92,6 +114,112 @@ const MyList = () => {
92
114
  ```
93
115
 
94
116
 
117
+ ### Router
118
+ The **vite-plugin-vanjs** provides a router with load and preload capability, code splitting and lazy loading, all via the exported `@vanjs/router` module. This functionality is still in early stages, but it manages to work with both Server Side Rendering (SSR) when using an appropriate starter template, as well as Client Side Rendering (CSR/SPA - your classic VanJS app), as we'll see in the example below.
119
+
120
+ Here's a basic example, let's start with the `app.ts`:
121
+
122
+ ```ts
123
+ // src/app.ts
124
+ import van from "vanjs-core";
125
+ import { Router, Route, A } from "@vanjs/router";
126
+
127
+ // define routes
128
+ Route({ path: '/', component: () => van.tags.div('Hello VanJS') });
129
+ Route({ path: '/about', component: lazy(() => import('./pages/about'))});
130
+ Route({ path: '*', component: () => van.tags.div('404 - Page Not Found') });
131
+
132
+ function App() {
133
+ // the Router is only an outlet
134
+ export default Route();
135
+ }
136
+
137
+ // render or hydrate the app
138
+ van.add(document.body, App());
139
+ ```
140
+
141
+ Here's how a page should look like, pay attention to the comments:
142
+ ```ts
143
+ // src/pages/about.ts
144
+ import van from "vanjs-core";
145
+ import { A } from "@vanjs/router";
146
+
147
+ // define routes
148
+ export const route = {
149
+ preload: async () => {
150
+ // in most cases you may want to enforce user access control
151
+ console.log('About preload triggered');
152
+ },
153
+ load: async () => {
154
+ // Load data if needed
155
+ // you might want to cache this data
156
+ console.log('About load triggered');
157
+ }
158
+ }
159
+
160
+ // you must export your page component as either
161
+ // a named export "Page" or a default export
162
+ export function Page() {
163
+ const { div, h1, p, br } = van.tags;
164
+ return div(
165
+ h1('About'),
166
+ p('This is the about page'),
167
+ A({ href: "/" }, "Back to Home")
168
+ )
169
+ }
170
+ ```
171
+ **Notes**
172
+ - when hovering the `A` component, if it links to a lazy component it will trigger that page component preload, but not preload any data;
173
+ - if you use the regular `a` from `van.tags` instead of the `A` component, your application will work as a classic Multi-Page App (MPA).
174
+
175
+
176
+ ### Metadata
177
+ The **vite-plugin-vanjs** also provides metadata management via the exported `@vanjs/meta` module. This module works with both SSR and CSR, and makes it easy to work with.
178
+
179
+ Depending on the type of application, it's generally very easy to setup the system to work properly, we only need to make sure that we execute functions in a specific order:
180
+
181
+ * first we call the `initializeHeadTags` which allows the module to store the current tags that come from either a vite starter template `index.html` or the server (SSR); if we don't call this function, the existing tags will be removed and some of them are important (especially `charset` and `viewport`);
182
+ * then we define a set of default tags to be used by both client and server (if using an SSR template) on all pages that don't come with any metadata tags;
183
+ * lastly, on other pages, we define tags that override both the existing and the default tags
184
+
185
+ Ok let's start again with the `app.ts`:
186
+ ```ts
187
+ import van from 'vanjs-core'
188
+ import { Style, Title, Meta, initializeHeadTags } from '@vanjs/meta'
189
+
190
+ function App() {
191
+ const { div, h1, p } van.tags;
192
+
193
+ // when app is first time executed
194
+ // we need to register tags coming
195
+ // from the index.html template
196
+ initializeHeadTags();
197
+
198
+ // a good practice is to define some default tags
199
+ // they are used on pages where no tags are set
200
+ Title("VanJS + Vite App");
201
+ Meta({ name: "description", content: "Sample app description" });
202
+ Meta({ property: "og:description", content: "Some open graph description for your app" });
203
+ Link({ href: 'path-to/your-style.css', rel: "stylesheet" });
204
+ Script({ src: 'path-to/your-script.js' });
205
+ Style({ id: "app-style" },
206
+ `p { margin-bottom: 1rem }`
207
+ );
208
+
209
+ return div(
210
+ h1('Hello VanJS!'),
211
+ p('Sample paragraph.')
212
+ );
213
+ }
214
+
215
+ // render or hydrate the app
216
+ van.add(document.body, App());
217
+ ```
218
+ **Note**
219
+ - the `Style` component doesn't have any unique attribute so we must use a unique ID to prevent duplicates;
220
+ - all provided metadata components don't return any markup, their function is to register new tags or update existing tags.
221
+
222
+
95
223
  ### JSX Transformation
96
224
 
97
225
  To enable JSX transformation, you have to edit your `tsconfig.json` as follows:
@@ -119,24 +247,24 @@ const App = () => {
119
247
  );
120
248
  }
121
249
 
122
- van.add(document.getElementById("app")!, <App /> as ChildDom);
250
+ const root = document.getElementById("app") as HTMLElement;
123
251
 
252
+ van.add(root, <App />);
124
253
  ```
125
254
 
126
255
  **Notes**:
127
- * in some cases like this one, enforcing a certain typescript type via `as` might be in good order depending on who renders your component;
256
+ * in cases like this one, enforcing a certain typescript type via `as` might be in good order depending on who renders your component;
128
257
  * you can use `ref` as a `van.state`, `class` instead of `className`, `for` instead of `htmlFor`;
129
258
  * you can use style as both an object and a string;
130
259
  * for a JSX starter template, check out the [vite-starter-vanjs-ssr-jsx](https://github.com/thednp/vite-starter-vanjs-ssr-jsx).
131
260
  * for a pure vanilla starter template, check out the [vite-starter-vanjs-ssr](https://github.com/thednp/vite-starter-vanjs-ssr).
132
261
 
133
262
 
134
-
135
263
  ### Credits
136
264
  * [van-jsx](https://github.com/herudi/van-jsx) a simple Vanilla JSX implementation;
137
265
  * [vanjs-jsx](https://github.com/vanjs-org/van/tree/main/addons/van_jsx) the official VanJS addon;
138
266
  * [surplus](https://github.com/adamhaile/surplus/blob/master/index.d.ts) for Typescript definitions also used by SolidJS;
139
- * [inferno](https://github.com/infernojs/inferno/blob/master/packages/inferno/src/core/types.ts) also typescript definitions.
267
+ * [inferno](https://github.com/infernojs/inferno/blob/master/packages/inferno/src/core/types.ts) also for typescript definitions.
140
268
 
141
269
 
142
270
  ### License
@@ -0,0 +1,41 @@
1
+ declare module "@vanjs/client" {
2
+ import * as CSS from "csstype";
3
+ import type {
4
+ Element as VanElement,
5
+ TagFunc,
6
+ } from "mini-van-plate/van-plate";
7
+
8
+ /**
9
+ * Sets the attribute value of the given name of the given element.
10
+ *
11
+ * @param element the target element
12
+ * @param key the attribute name
13
+ * @param value the attribute value
14
+ */
15
+ export const setAttribute: (
16
+ element: Element,
17
+ name: string,
18
+ value: boolean | string | number | null | undefined,
19
+ ) => void;
20
+
21
+ /**
22
+ * Normalize the style value and convert it to a string
23
+ *
24
+ * @param source the style value
25
+ * @returns the normalized style string
26
+ */
27
+ export const styleToString: (source: string | CSS.Properties) => string;
28
+
29
+ type ChildElement = TagFunc | VanElement | HTMLElement | SVGElement;
30
+
31
+ /**
32
+ * Hydrate the target element with the given content(s).
33
+ *
34
+ * @param target the target element
35
+ * @param content the element(s) to hydrate
36
+ */
37
+ export const hydrate: (
38
+ target: HTMLElement,
39
+ content: ChildElement | ChildElement[],
40
+ ) => HTMLElement;
41
+ }
@@ -0,0 +1,48 @@
1
+ import { unwrap } from "@vanjs/router";
2
+
3
+ /**
4
+ * @param {Element} element
5
+ * @param {string} key
6
+ * @param {boolean | string | number | null | undefined} value
7
+ */
8
+ export const setAttribute = (element, key, value) => {
9
+ if (value == null || value === false || value === "" || value === undefined) {
10
+ element.removeAttribute(key);
11
+ } else {
12
+ const attr = value === true ? "" : String(value);
13
+ element.setAttribute(key, attr);
14
+ }
15
+ };
16
+
17
+ /**
18
+ * @param {import("csstype").Properties | string} style
19
+ * @returns {string}
20
+ */
21
+ export const styleToString = (style) => {
22
+ return typeof style === "string"
23
+ ? style
24
+ : typeof style === "object"
25
+ ? Object.entries(style).reduce((acc, [key, value]) =>
26
+ acc +
27
+ key
28
+ .split(/(?=[A-Z])/)
29
+ .join("-")
30
+ .toLowerCase() +
31
+ ":" +
32
+ // allow state values in style object
33
+ (typeof value === "object" && "val" in value ? value.val : value) +
34
+ ";", "")
35
+ : /* istanbul ignore next */ "";
36
+ };
37
+
38
+ /**
39
+ * @param {Element} target the root element
40
+ * @param {Element | Element[]} content the element(s) to hydrate
41
+ */
42
+ export const hydrate = (target, content) => {
43
+ const wrapper = unwrap(content);
44
+ target.replaceChildren(
45
+ ...(Array.from(wrapper.children)),
46
+ );
47
+ return target;
48
+ };
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "client",
3
+ "main": "./index.mjs",
4
+ "module": "./index.mjs",
5
+ "types": "./types.d.ts",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./types.d.ts",
10
+ "import": "./index.mjs"
11
+ }
12
+ },
13
+ "peerDependencies": {
14
+ "vite-plugin-vanjs": "*",
15
+ "csstype": "*"
16
+ },
17
+ "sideEffects": false
18
+ }
@@ -0,0 +1,34 @@
1
+ /// <reference path="global.d.ts" />
2
+ import * as CSS from "csstype";
3
+
4
+ /**
5
+ * Sets the attribute value of the given name of the given element.
6
+ *
7
+ * @param element the target element
8
+ * @param key the attribute name
9
+ * @param value the attribute value
10
+ */
11
+ export const setAttribute: (
12
+ element: Element,
13
+ name: string,
14
+ value: boolean | string | number | null | undefined,
15
+ ) => void;
16
+
17
+ /**
18
+ * Normalize the style value and convert it to a string
19
+ *
20
+ * @param source the style value
21
+ * @returns the normalized style string
22
+ */
23
+ export const styleToString: (source: string | CSS.Properties) => string;
24
+
25
+ /**
26
+ * Hydrate a target element with the given content(s).
27
+ *
28
+ * @param target the root element
29
+ * @param content the element(s) to hydrate
30
+ */
31
+ export const hydrate: (
32
+ target: HTMLElement,
33
+ content: HTMLElement | HTMLElement[],
34
+ ) => HTMLElement;
package/jsx/global.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  declare module "@vanjs/jsx" {
2
2
  import type { ChildDom, Primitive } from "vanjs-core";
3
+ import type { Element as VanElement } from "mini-van-plate/van-plate";
3
4
  export const Fragment = ({ children }: { children?: JSX.Element }) =>
4
5
  JSX.Element;
5
- export type VanElement = Element;
6
+ // export type VanElement = Element;
6
7
  export type VanNode =
7
8
  | Primitive
8
9
  | ChildDom
@@ -12,12 +13,12 @@ declare module "@vanjs/jsx" {
12
13
  | null;
13
14
 
14
15
  export function jsx(
15
- tag: string | ((props: any) => VanNode),
16
+ tag: string | ((props: unknown) => VanNode),
16
17
  props: {
17
18
  children?: VanNode;
18
19
  style?: string | CSSProperties | (() => CSSProperties);
19
20
  ref?: State<Element>;
20
- [key: string]: any;
21
+ [key: string]: unknown;
21
22
  },
22
23
  ): VanNode;
23
24
 
package/jsx/jsx.d.ts CHANGED
@@ -1,5 +1,7 @@
1
+ // deno-lint-ignore-file no-empty-interface ban-types
2
+
1
3
  import * as csstype from "csstype";
2
- import type { ChildDom, Primitive, State } from "vanjs-core";
4
+ import type { ChildDom, State } from "vanjs-core";
3
5
 
4
6
  /**
5
7
  * Based on JSX types for Surplus and Inferno and adapted for `dom-expressions`.
@@ -16,7 +18,7 @@ export = JSX;
16
18
 
17
19
  declare namespace JSX {
18
20
  // type FunctionMaybe<T = unknown> = { (): T } | T;
19
- type FunctionMaybe<T = unknown> = State<T> | T;
21
+ type FunctionMaybe<T = unknown> = (() => T) | State<T> | T;
20
22
  type Element =
21
23
  | ChildDom
22
24
  | Node
@@ -52,13 +54,13 @@ declare namespace JSX {
52
54
 
53
55
  interface BoundEventHandler<T, E extends Event> {
54
56
  0: (
55
- data: any,
57
+ data: unknown,
56
58
  e: E & {
57
59
  currentTarget: T;
58
60
  target: DOMElement;
59
61
  },
60
62
  ) => void;
61
- 1: any;
63
+ 1: unknown;
62
64
  }
63
65
  type EventHandlerUnion<T, E extends Event> =
64
66
  | EventHandler<T, E>
@@ -85,10 +87,12 @@ declare namespace JSX {
85
87
  }
86
88
 
87
89
  interface IntrinsicAttributes {
88
- ref?: unknown | ((e: unknown) => void);
90
+ // ref?: unknown | ((e: unknown) => void);
91
+ ref?: State<T>;
89
92
  }
90
93
  interface CustomAttributes<T> {
91
- ref?: State<T> | T | ((el: T) => void);
94
+ // ref?: State<T> | T | ((el: T) => void);
95
+ ref?: State<T>;
92
96
  }
93
97
  interface ExplicitProperties {}
94
98
  interface ExplicitAttributes {}
@@ -295,7 +299,11 @@ declare namespace JSX {
295
299
 
296
300
  interface CSSProperties extends csstype.PropertiesHyphen {
297
301
  // Override
298
- [key: `-${string}`]: string | number | undefined;
302
+ [key: `-${string}`]:
303
+ | State<string | number | undefined>
304
+ | string
305
+ | number
306
+ | undefined;
299
307
  }
300
308
 
301
309
  type HTMLAutocapitalize =
@@ -719,7 +727,7 @@ declare namespace JSX {
719
727
  translate?: FunctionMaybe<"yes" | "no">;
720
728
  about?: FunctionMaybe<string>;
721
729
  datatype?: FunctionMaybe<string>;
722
- inlist?: FunctionMaybe<any>;
730
+ inlist?: FunctionMaybe<unknown>;
723
731
  popover?: FunctionMaybe<boolean | "manual" | "auto">;
724
732
  prefix?: FunctionMaybe<string>;
725
733
  property?: FunctionMaybe<string>;
@@ -768,7 +776,7 @@ declare namespace JSX {
768
776
  >;
769
777
  }
770
778
  interface AnchorHTMLAttributes<T> extends HTMLAttributes<T> {
771
- download?: FunctionMaybe<any>;
779
+ download?: FunctionMaybe<unknown>;
772
780
  href?: FunctionMaybe<string>;
773
781
  hreflang?: FunctionMaybe<string>;
774
782
  media?: FunctionMaybe<string>;
@@ -783,7 +791,7 @@ declare namespace JSX {
783
791
  interface AreaHTMLAttributes<T> extends HTMLAttributes<T> {
784
792
  alt?: FunctionMaybe<string>;
785
793
  coords?: FunctionMaybe<string>;
786
- download?: FunctionMaybe<any>;
794
+ download?: FunctionMaybe<unknown>;
787
795
  href?: FunctionMaybe<string>;
788
796
  hreflang?: FunctionMaybe<string>;
789
797
  ping?: FunctionMaybe<string>;
package/jsx/jsx.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import van from "@vanjs/van";
2
2
  import setup from "@vanjs/setup";
3
- import { setAttribute, styleToString } from "./utils.mjs";
3
+ import { setAttribute, styleToString } from "@vanjs/client";
4
4
 
5
5
  export const jsx = (jsxTag, { children, ref, style, ...props }) => {
6
6
  if (typeof jsxTag === "string") {
@@ -28,6 +28,7 @@ export const jsx = (jsxTag, { children, ref, style, ...props }) => {
28
28
 
29
29
  setAttribute(newElement, k, value);
30
30
  }
31
+ /* istanbul ignore else */
31
32
  if (style) {
32
33
  if (typeof style === "function") {
33
34
  van.derive(() => newElement.style.cssText = styleToString(style()));
@@ -42,5 +43,5 @@ export const jsx = (jsxTag, { children, ref, style, ...props }) => {
42
43
 
43
44
  return typeof jsxTag === "function"
44
45
  ? jsxTag({ children, ref, style, ...props })
45
- : null;
46
+ : /* istanbul ignore next */ null;
46
47
  };
package/jsx/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "exports": {
5
5
  ".": {
6
- "types": "./index.d.ts",
6
+ "types": "./types.d.ts",
7
7
  "import": "./index.mjs"
8
8
  },
9
9
  "./jsx-runtime": {
package/jsx/types.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /// <reference path="global.d.ts" />
2
+ import type { Fragment } from "./fragment";
3
+
4
+ declare function jsx(
5
+ type: unknown,
6
+ props: unknown,
7
+ ): () =>
8
+ | (Node & {
9
+ [key: string]: unknown;
10
+ })
11
+ | (Node & {
12
+ [key: string]: unknown;
13
+ })[];
14
+
15
+ export type { JSX } from "./jsx";
16
+ export { Fragment, jsx, jsx as jsxDEV, jsx as jsxs };
package/meta/Head.mjs ADDED
@@ -0,0 +1,81 @@
1
+ import setup from "@vanjs/setup";
2
+ import { getTagKey } from "./helpers.mjs";
3
+
4
+ /** @typedef {typeof import("./types.d.ts").getHeadTags} GetTags */
5
+ /** @typedef {typeof import("./types.d.ts").createHeadTags} CreateTags */
6
+ /** @typedef {typeof import("./types.d.ts").resetHeadTags} ResetTags */
7
+ /** @typedef {typeof import("./types.d.ts").initializeHeadTags} InitializeTags */
8
+ /** @typedef {import("./types.d.ts").AddMeta} AddMeta */
9
+ /** @typedef {typeof import("./types.d.ts").Head} HeadComp */
10
+
11
+ /**
12
+ * Create a new Map for each request on server
13
+ * @type {CreateTags}
14
+ */
15
+ const createHeadTags = () => new Map();
16
+
17
+ /**
18
+ * Get the current head tags. Use a factory pattern to get the right store
19
+ * on the server and the client.
20
+ * @type {GetTags}
21
+ */
22
+ const getHeadTags = (() => {
23
+ if (setup.isServer) {
24
+ // On server, create one Map per request scope
25
+ let serverHeadTags;
26
+ return () => {
27
+ if (!serverHeadTags) {
28
+ serverHeadTags = createHeadTags();
29
+ }
30
+ return serverHeadTags;
31
+ };
32
+ }
33
+ // On client, use a singleton
34
+ const clientHeadTags = createHeadTags();
35
+ return () => clientHeadTags;
36
+ })();
37
+
38
+ /**
39
+ * Clear all head tags
40
+ * @type {ResetTags}
41
+ */
42
+ export const resetHeadTags = () => {
43
+ const tags = getHeadTags();
44
+ tags.clear();
45
+ };
46
+
47
+ /**
48
+ * Initialize the head tags
49
+ * @type {InitializeTags}
50
+ */
51
+ export const initializeHeadTags = () => {
52
+ const tags = getHeadTags();
53
+ /* istanbul ignore else */
54
+ if (!tags.size && !setup.isServer) {
55
+ [...document.head.children].forEach((tag) => {
56
+ tags.set(getTagKey(tag), tag);
57
+ });
58
+ }
59
+ };
60
+
61
+ /**
62
+ * Add a new meta tag
63
+ * @type {AddMeta}
64
+ */
65
+ export const addMeta = (tag) => {
66
+ if (!tag) return;
67
+ const tags = getHeadTags();
68
+ const key = getTagKey(tag);
69
+ tags.set(key, tag);
70
+ };
71
+
72
+ /**
73
+ * Get the head tags
74
+ * @type {HeadComp}
75
+ */
76
+ export const Head = () => {
77
+ return () => {
78
+ const tags = getHeadTags();
79
+ return Array.from(tags.values());
80
+ };
81
+ };
@@ -0,0 +1,60 @@
1
+ declare module "@vanjs/meta" {
2
+ import { type PropsWithKnownKeys } from "vanjs-core";
3
+ import type {
4
+ Element as VanElement,
5
+ TagFunc,
6
+ } from "mini-van-plate/van-plate";
7
+
8
+ export const createHeadTags: () => Map<
9
+ string,
10
+ SupportedTags | TagFunc
11
+ >;
12
+ export const getHeadTags: () => Map<
13
+ string,
14
+ SupportedTags | TagFunc
15
+ >;
16
+ export type ResetTags = () => void;
17
+ export type InitializeTags = () => void;
18
+
19
+ export type SupportedTags =
20
+ | HTMLTitleElement
21
+ | HTMLMetaElement
22
+ | HTMLScriptElement
23
+ | HTMLLinkElement
24
+ | HTMLStyleElement;
25
+
26
+ export type TagProps = PropsWithKnownKeys<SupportedTags>;
27
+
28
+ export type HeadTags = SupportedTags[] | TagFunc[];
29
+
30
+ export type AddMeta = (tag?: TagProps) => void;
31
+
32
+ // export declare const Head: () => () => TagFunc[];
33
+ export declare const Head: () => () => SupportedTags[];
34
+ export const Title: (
35
+ props: PropsWithKnownKeys<HTMLTitleElement>,
36
+ content?: string,
37
+ ) => null;
38
+ export const Meta: (props: PropsWithKnownKeys<HTMLMetaElement>) => null;
39
+ export const Style: (
40
+ props: PropsWithKnownKeys<HTMLStyleElement>,
41
+ content?: string,
42
+ ) => null;
43
+ export const Link: (props: PropsWithKnownKeys<HTMLLinkElement>) => null;
44
+ export const Script: (
45
+ props: PropsWithKnownKeys<HTMLScriptElement>,
46
+ content?: string,
47
+ ) => null;
48
+
49
+ export type ParseAttributes = (
50
+ attributeString: string,
51
+ ) => Record<string, string>;
52
+
53
+ export type GetTagAttribute = (tag: TagProps) => string;
54
+
55
+ export type GetTagKey = (tag: TagProps) => string;
56
+
57
+ export const initializeHeadTags: InitializeTags;
58
+ export const resetHeadTags: ResetTags;
59
+ export const addMeta: AddMeta;
60
+ }