@unhead/react 3.0.0-beta.4 → 3.0.0-beta.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/dist/client.d.mts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.mjs +5 -7
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.mjs +1 -1
- package/dist/stream/client.d.mts +15 -0
- package/dist/stream/client.d.ts +15 -0
- package/dist/stream/client.mjs +12 -0
- package/dist/stream/server.d.mts +56 -0
- package/dist/stream/server.d.ts +56 -0
- package/dist/stream/server.mjs +51 -0
- package/dist/stream/vite.d.mts +27 -0
- package/dist/stream/vite.d.ts +27 -0
- package/dist/stream/vite.mjs +86 -0
- package/package.json +35 -4
package/dist/client.d.mts
CHANGED
|
@@ -9,6 +9,6 @@ declare function createHead(options?: CreateClientHeadOptions): Unhead;
|
|
|
9
9
|
declare function UnheadProvider({ children, head }: {
|
|
10
10
|
children: ReactNode;
|
|
11
11
|
head?: ReturnType<typeof createHead>;
|
|
12
|
-
}): react.FunctionComponentElement<react.ProviderProps<Unhead<unhead_types.ResolvableHead> | null>>;
|
|
12
|
+
}): react.FunctionComponentElement<react.ProviderProps<Unhead<unhead_types.ResolvableHead, unknown> | null>>;
|
|
13
13
|
|
|
14
14
|
export { UnheadProvider, createHead };
|
package/dist/client.d.ts
CHANGED
|
@@ -9,6 +9,6 @@ declare function createHead(options?: CreateClientHeadOptions): Unhead;
|
|
|
9
9
|
declare function UnheadProvider({ children, head }: {
|
|
10
10
|
children: ReactNode;
|
|
11
11
|
head?: ReturnType<typeof createHead>;
|
|
12
|
-
}): react.FunctionComponentElement<react.ProviderProps<Unhead<unhead_types.ResolvableHead> | null>>;
|
|
12
|
+
}): react.FunctionComponentElement<react.ProviderProps<Unhead<unhead_types.ResolvableHead, unknown> | null>>;
|
|
13
13
|
|
|
14
14
|
export { UnheadProvider, createHead };
|
package/dist/client.mjs
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { createElement } from 'react';
|
|
2
|
-
import { createHead as createHead$1
|
|
2
|
+
import { createDomRenderer, createDebouncedFn, createHead as createHead$1 } from 'unhead/client';
|
|
3
3
|
export { renderDOMHead } from 'unhead/client';
|
|
4
4
|
import { U as UnheadContext } from './shared/react.DF9T1fqs.mjs';
|
|
5
5
|
|
|
6
6
|
function createHead(options = {}) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
...options
|
|
12
|
-
});
|
|
7
|
+
const domRenderer = createDomRenderer();
|
|
8
|
+
let head;
|
|
9
|
+
const debouncedRenderer = createDebouncedFn(() => domRenderer(head), (fn) => setTimeout(fn, 0));
|
|
10
|
+
head = createHead$1({ render: debouncedRenderer, ...options });
|
|
13
11
|
return head;
|
|
14
12
|
}
|
|
15
13
|
function UnheadProvider({ children, head }) {
|
package/dist/server.d.mts
CHANGED
|
@@ -3,11 +3,11 @@ import { ReactNode } from 'react';
|
|
|
3
3
|
import * as unhead_types from 'unhead/types';
|
|
4
4
|
import { Unhead } from 'unhead/types';
|
|
5
5
|
export { CreateServerHeadOptions, SSRHeadPayload, Unhead } from 'unhead/types';
|
|
6
|
-
export { createHead,
|
|
6
|
+
export { createHead, renderSSRHead, transformHtmlTemplate } from 'unhead/server';
|
|
7
7
|
|
|
8
8
|
declare function UnheadProvider({ children, value }: {
|
|
9
9
|
children: ReactNode;
|
|
10
10
|
value: Unhead;
|
|
11
|
-
}): react.FunctionComponentElement<react.ProviderProps<Unhead<unhead_types.ResolvableHead> | null>>;
|
|
11
|
+
}): react.FunctionComponentElement<react.ProviderProps<Unhead<unhead_types.ResolvableHead, unknown> | null>>;
|
|
12
12
|
|
|
13
13
|
export { UnheadProvider };
|
package/dist/server.d.ts
CHANGED
|
@@ -3,11 +3,11 @@ import { ReactNode } from 'react';
|
|
|
3
3
|
import * as unhead_types from 'unhead/types';
|
|
4
4
|
import { Unhead } from 'unhead/types';
|
|
5
5
|
export { CreateServerHeadOptions, SSRHeadPayload, Unhead } from 'unhead/types';
|
|
6
|
-
export { createHead,
|
|
6
|
+
export { createHead, renderSSRHead, transformHtmlTemplate } from 'unhead/server';
|
|
7
7
|
|
|
8
8
|
declare function UnheadProvider({ children, value }: {
|
|
9
9
|
children: ReactNode;
|
|
10
10
|
value: Unhead;
|
|
11
|
-
}): react.FunctionComponentElement<react.ProviderProps<Unhead<unhead_types.ResolvableHead> | null>>;
|
|
11
|
+
}): react.FunctionComponentElement<react.ProviderProps<Unhead<unhead_types.ResolvableHead, unknown> | null>>;
|
|
12
12
|
|
|
13
13
|
export { UnheadProvider };
|
package/dist/server.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createElement } from 'react';
|
|
2
2
|
import { U as UnheadContext } from './shared/react.DF9T1fqs.mjs';
|
|
3
|
-
export { createHead,
|
|
3
|
+
export { createHead, renderSSRHead, transformHtmlTemplate } from 'unhead/server';
|
|
4
4
|
|
|
5
5
|
function UnheadProvider({ children, value }) {
|
|
6
6
|
return createElement(UnheadContext.Provider, { value }, children);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { Unhead } from 'unhead/types';
|
|
3
|
+
export { CreateStreamableClientHeadOptions, UnheadStreamQueue, createStreamableHead } from 'unhead/stream/client';
|
|
4
|
+
|
|
5
|
+
declare function UnheadProvider({ value, children }: {
|
|
6
|
+
value: Unhead;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}): ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* Client-side HeadStream - renders empty script with suppressHydrationWarning
|
|
11
|
+
* to match server-side structure without hydration mismatch errors.
|
|
12
|
+
*/
|
|
13
|
+
declare function HeadStream(): ReactNode;
|
|
14
|
+
|
|
15
|
+
export { HeadStream, UnheadProvider };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { Unhead } from 'unhead/types';
|
|
3
|
+
export { CreateStreamableClientHeadOptions, UnheadStreamQueue, createStreamableHead } from 'unhead/stream/client';
|
|
4
|
+
|
|
5
|
+
declare function UnheadProvider({ value, children }: {
|
|
6
|
+
value: Unhead;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}): ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* Client-side HeadStream - renders empty script with suppressHydrationWarning
|
|
11
|
+
* to match server-side structure without hydration mismatch errors.
|
|
12
|
+
*/
|
|
13
|
+
declare function HeadStream(): ReactNode;
|
|
14
|
+
|
|
15
|
+
export { HeadStream, UnheadProvider };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import { U as UnheadContext } from '../shared/react.DF9T1fqs.mjs';
|
|
3
|
+
export { createStreamableHead } from 'unhead/stream/client';
|
|
4
|
+
|
|
5
|
+
function UnheadProvider({ value, children }) {
|
|
6
|
+
return createElement(UnheadContext.Provider, { value }, children);
|
|
7
|
+
}
|
|
8
|
+
function HeadStream() {
|
|
9
|
+
return createElement("script", { suppressHydrationWarning: true });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { HeadStream, UnheadProvider };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Writable } from 'node:stream';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { StreamableHeadContext, CreateStreamableServerHeadOptions } from 'unhead/stream/server';
|
|
4
|
+
export { BaseStreamableHeadContext, CreateStreamableServerHeadOptions, StreamableHeadContext, StreamingTemplateParts, WebStreamableHeadContext, prepareStreamingTemplate, renderSSRHeadShell, renderSSRHeadSuspenseChunk, wrapStream } from 'unhead/stream/server';
|
|
5
|
+
import { Unhead, ResolvableHead } from 'unhead/types';
|
|
6
|
+
|
|
7
|
+
declare function UnheadProvider({ value, children }: {
|
|
8
|
+
value: Unhead;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}): ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* Streaming head component for React.
|
|
13
|
+
* Place inside Suspense boundaries after async components that use useHead.
|
|
14
|
+
*/
|
|
15
|
+
declare function HeadStream(): ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* A pipe function from React's renderToPipeableStream
|
|
18
|
+
*/
|
|
19
|
+
type ReactPipeFunction = (writable: Writable) => void;
|
|
20
|
+
/**
|
|
21
|
+
* React-specific context returned by createStreamableHead.
|
|
22
|
+
* Extends core StreamableHeadContext with React's wrap helper.
|
|
23
|
+
*/
|
|
24
|
+
interface ReactStreamableHeadContext<T = ResolvableHead> extends Pick<StreamableHeadContext<T>, 'head' | 'onShellReady'> {
|
|
25
|
+
/**
|
|
26
|
+
* Wrap React's pipe function to handle head injection automatically
|
|
27
|
+
* @param pipe - The pipe function from renderToPipeableStream
|
|
28
|
+
* @param template - The HTML template (from Vite's transformIndexHtml)
|
|
29
|
+
* @returns A new pipe function that handles shell rendering
|
|
30
|
+
*/
|
|
31
|
+
wrap: (pipe: ReactPipeFunction, template: string) => (writable: Writable) => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Creates a head instance configured for React streaming SSR.
|
|
35
|
+
*
|
|
36
|
+
* Returns a context with:
|
|
37
|
+
* - `head`: The Unhead instance for UnheadProvider
|
|
38
|
+
* - `onShellReady`: Callback to pass to renderToPipeableStream
|
|
39
|
+
* - `wrap`: Wraps React's pipe to handle head injection
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const { head, onShellReady, wrap } = createStreamableHead()
|
|
44
|
+
*
|
|
45
|
+
* const { pipe } = renderToPipeableStream(
|
|
46
|
+
* <UnheadProvider value={head}><App /></UnheadProvider>,
|
|
47
|
+
* { onShellReady }
|
|
48
|
+
* )
|
|
49
|
+
*
|
|
50
|
+
* return { pipe: wrap(pipe, template) }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
declare function createStreamableHead<T = ResolvableHead>(options?: CreateStreamableServerHeadOptions): ReactStreamableHeadContext<T>;
|
|
54
|
+
|
|
55
|
+
export { HeadStream, UnheadProvider, createStreamableHead };
|
|
56
|
+
export type { ReactStreamableHeadContext };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Writable } from 'node:stream';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { StreamableHeadContext, CreateStreamableServerHeadOptions } from 'unhead/stream/server';
|
|
4
|
+
export { BaseStreamableHeadContext, CreateStreamableServerHeadOptions, StreamableHeadContext, StreamingTemplateParts, WebStreamableHeadContext, prepareStreamingTemplate, renderSSRHeadShell, renderSSRHeadSuspenseChunk, wrapStream } from 'unhead/stream/server';
|
|
5
|
+
import { Unhead, ResolvableHead } from 'unhead/types';
|
|
6
|
+
|
|
7
|
+
declare function UnheadProvider({ value, children }: {
|
|
8
|
+
value: Unhead;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}): ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* Streaming head component for React.
|
|
13
|
+
* Place inside Suspense boundaries after async components that use useHead.
|
|
14
|
+
*/
|
|
15
|
+
declare function HeadStream(): ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* A pipe function from React's renderToPipeableStream
|
|
18
|
+
*/
|
|
19
|
+
type ReactPipeFunction = (writable: Writable) => void;
|
|
20
|
+
/**
|
|
21
|
+
* React-specific context returned by createStreamableHead.
|
|
22
|
+
* Extends core StreamableHeadContext with React's wrap helper.
|
|
23
|
+
*/
|
|
24
|
+
interface ReactStreamableHeadContext<T = ResolvableHead> extends Pick<StreamableHeadContext<T>, 'head' | 'onShellReady'> {
|
|
25
|
+
/**
|
|
26
|
+
* Wrap React's pipe function to handle head injection automatically
|
|
27
|
+
* @param pipe - The pipe function from renderToPipeableStream
|
|
28
|
+
* @param template - The HTML template (from Vite's transformIndexHtml)
|
|
29
|
+
* @returns A new pipe function that handles shell rendering
|
|
30
|
+
*/
|
|
31
|
+
wrap: (pipe: ReactPipeFunction, template: string) => (writable: Writable) => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Creates a head instance configured for React streaming SSR.
|
|
35
|
+
*
|
|
36
|
+
* Returns a context with:
|
|
37
|
+
* - `head`: The Unhead instance for UnheadProvider
|
|
38
|
+
* - `onShellReady`: Callback to pass to renderToPipeableStream
|
|
39
|
+
* - `wrap`: Wraps React's pipe to handle head injection
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const { head, onShellReady, wrap } = createStreamableHead()
|
|
44
|
+
*
|
|
45
|
+
* const { pipe } = renderToPipeableStream(
|
|
46
|
+
* <UnheadProvider value={head}><App /></UnheadProvider>,
|
|
47
|
+
* { onShellReady }
|
|
48
|
+
* )
|
|
49
|
+
*
|
|
50
|
+
* return { pipe: wrap(pipe, template) }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
declare function createStreamableHead<T = ResolvableHead>(options?: CreateStreamableServerHeadOptions): ReactStreamableHeadContext<T>;
|
|
54
|
+
|
|
55
|
+
export { HeadStream, UnheadProvider, createStreamableHead };
|
|
56
|
+
export type { ReactStreamableHeadContext };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { PassThrough } from 'node:stream';
|
|
2
|
+
import { createElement, useContext } from 'react';
|
|
3
|
+
import { renderSSRHeadSuspenseChunk, createStreamableHead as createStreamableHead$1, prepareStreamingTemplate } from 'unhead/stream/server';
|
|
4
|
+
export { prepareStreamingTemplate, renderSSRHeadShell, renderSSRHeadSuspenseChunk, wrapStream } from 'unhead/stream/server';
|
|
5
|
+
import { U as UnheadContext } from '../shared/react.DF9T1fqs.mjs';
|
|
6
|
+
|
|
7
|
+
function UnheadProvider({ value, children }) {
|
|
8
|
+
return createElement(UnheadContext.Provider, { value }, children);
|
|
9
|
+
}
|
|
10
|
+
function HeadStream() {
|
|
11
|
+
const head = useContext(UnheadContext);
|
|
12
|
+
if (!head) {
|
|
13
|
+
throw new Error("HeadStream: head context not found");
|
|
14
|
+
}
|
|
15
|
+
const update = renderSSRHeadSuspenseChunk(head);
|
|
16
|
+
return createElement("script", {
|
|
17
|
+
suppressHydrationWarning: true,
|
|
18
|
+
dangerouslySetInnerHTML: update ? { __html: update } : void 0
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function createStreamableHead(options = {}) {
|
|
22
|
+
const { head, onShellReady, shellReady } = createStreamableHead$1(options);
|
|
23
|
+
return {
|
|
24
|
+
head,
|
|
25
|
+
onShellReady,
|
|
26
|
+
wrap: (pipe, template) => {
|
|
27
|
+
return (writable) => {
|
|
28
|
+
shellReady.then(async () => {
|
|
29
|
+
try {
|
|
30
|
+
const { shell, end } = await prepareStreamingTemplate(head, template);
|
|
31
|
+
writable.write(shell);
|
|
32
|
+
const passthrough = new PassThrough();
|
|
33
|
+
passthrough.on("data", (chunk) => writable.write(chunk));
|
|
34
|
+
passthrough.on("end", () => {
|
|
35
|
+
writable.write(end);
|
|
36
|
+
writable.end();
|
|
37
|
+
});
|
|
38
|
+
passthrough.on("error", (err) => {
|
|
39
|
+
writable.destroy(err);
|
|
40
|
+
});
|
|
41
|
+
pipe(passthrough);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
writable.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { HeadStream, UnheadProvider, createStreamableHead };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as vite from 'vite';
|
|
2
|
+
import { StreamingPluginOptions } from 'unhead/stream/vite';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vite plugin for React streaming SSR support.
|
|
6
|
+
* Automatically injects HeadStream components into React components that use useHead hooks.
|
|
7
|
+
*
|
|
8
|
+
* @returns Vite plugin configuration object with:
|
|
9
|
+
* - `name`: Plugin identifier
|
|
10
|
+
* - `enforce`: Plugin execution order ('pre')
|
|
11
|
+
* - `transform`: Transform hook for processing JSX/TSX files
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* // vite.config.ts
|
|
16
|
+
* import { unheadReactPlugin } from '@unhead/react/stream/vite'
|
|
17
|
+
*
|
|
18
|
+
* export default {
|
|
19
|
+
* plugins: [
|
|
20
|
+
* unheadReactPlugin()
|
|
21
|
+
* ]
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function unheadReactPlugin(options?: Pick<StreamingPluginOptions, 'mode'>): vite.Plugin<any>;
|
|
26
|
+
|
|
27
|
+
export { unheadReactPlugin as default, unheadReactPlugin };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as vite from 'vite';
|
|
2
|
+
import { StreamingPluginOptions } from 'unhead/stream/vite';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vite plugin for React streaming SSR support.
|
|
6
|
+
* Automatically injects HeadStream components into React components that use useHead hooks.
|
|
7
|
+
*
|
|
8
|
+
* @returns Vite plugin configuration object with:
|
|
9
|
+
* - `name`: Plugin identifier
|
|
10
|
+
* - `enforce`: Plugin execution order ('pre')
|
|
11
|
+
* - `transform`: Transform hook for processing JSX/TSX files
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* // vite.config.ts
|
|
16
|
+
* import { unheadReactPlugin } from '@unhead/react/stream/vite'
|
|
17
|
+
*
|
|
18
|
+
* export default {
|
|
19
|
+
* plugins: [
|
|
20
|
+
* unheadReactPlugin()
|
|
21
|
+
* ]
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function unheadReactPlugin(options?: Pick<StreamingPluginOptions, 'mode'>): vite.Plugin<any>;
|
|
26
|
+
|
|
27
|
+
export { unheadReactPlugin as default, unheadReactPlugin };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import { findStaticImports } from 'mlly';
|
|
3
|
+
import { parseSync, Visitor } from 'oxc-parser';
|
|
4
|
+
import { createStreamingPlugin } from 'unhead/stream/vite';
|
|
5
|
+
|
|
6
|
+
function transform(code, id, isSSR, s) {
|
|
7
|
+
const lang = id.endsWith(".tsx") ? "tsx" : id.endsWith(".jsx") ? "jsx" : "tsx";
|
|
8
|
+
const result = parseSync(id, code, { lang });
|
|
9
|
+
if (result.errors.length > 0)
|
|
10
|
+
return false;
|
|
11
|
+
const returns = [];
|
|
12
|
+
const visitor = new Visitor({
|
|
13
|
+
FunctionDeclaration: (node) => processFunction(node),
|
|
14
|
+
FunctionExpression: (node) => processFunction(node),
|
|
15
|
+
ArrowFunctionExpression: (node) => processFunction(node)
|
|
16
|
+
});
|
|
17
|
+
function processFunction(node) {
|
|
18
|
+
if (!node.body)
|
|
19
|
+
return;
|
|
20
|
+
const bodyCode = code.slice(node.body.start, node.body.end);
|
|
21
|
+
if (!bodyCode.includes("useHead") && !bodyCode.includes("useSeoMeta") && !bodyCode.includes("useHeadSafe"))
|
|
22
|
+
return;
|
|
23
|
+
if (node.body.type === "JSXElement" || node.body.type === "JSXFragment") {
|
|
24
|
+
returns.push({ jsxStart: node.body.start, jsxEnd: node.body.end });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const innerVisitor = new Visitor({
|
|
28
|
+
ReturnStatement(innerNode) {
|
|
29
|
+
if (!innerNode.argument)
|
|
30
|
+
return;
|
|
31
|
+
let arg = innerNode.argument;
|
|
32
|
+
if (arg.type === "ParenthesizedExpression" && arg.expression)
|
|
33
|
+
arg = arg.expression;
|
|
34
|
+
if (arg.type === "JSXElement" || arg.type === "JSXFragment")
|
|
35
|
+
returns.push({ jsxStart: arg.start, jsxEnd: arg.end });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
innerVisitor.visit(node.body);
|
|
39
|
+
}
|
|
40
|
+
visitor.visit(result.program);
|
|
41
|
+
if (returns.length === 0)
|
|
42
|
+
return false;
|
|
43
|
+
returns.sort((a, b) => b.jsxStart - a.jsxStart);
|
|
44
|
+
for (const ret of returns) {
|
|
45
|
+
const jsxCode = code.slice(ret.jsxStart, ret.jsxEnd);
|
|
46
|
+
s.overwrite(ret.jsxStart, ret.jsxEnd, `<><HeadStream />${jsxCode}</>`);
|
|
47
|
+
}
|
|
48
|
+
const importPath = isSSR ? "@unhead/react/stream/server" : "@unhead/react/stream/client";
|
|
49
|
+
const imports = findStaticImports(code);
|
|
50
|
+
const existing = imports.find((i) => i.specifier === importPath);
|
|
51
|
+
if (existing) {
|
|
52
|
+
if (!existing.imports?.includes("HeadStream")) {
|
|
53
|
+
const inner = existing.imports?.replace(/^\{\s*|\s*\}\s*$/g, "").trim() || "";
|
|
54
|
+
s.overwrite(existing.start, existing.end, `import { ${inner ? `${inner}, ` : ""}HeadStream } from '${importPath}'
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
const last = imports[imports.length - 1];
|
|
59
|
+
if (last)
|
|
60
|
+
s.appendLeft(last.end, `import { HeadStream } from '${importPath}'
|
|
61
|
+
`);
|
|
62
|
+
else
|
|
63
|
+
s.prepend(`import { HeadStream } from '${importPath}'
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
function unheadReactPlugin(options) {
|
|
69
|
+
return createStreamingPlugin({
|
|
70
|
+
framework: "@unhead/react",
|
|
71
|
+
mode: options?.mode,
|
|
72
|
+
transform(code, id, opts) {
|
|
73
|
+
if (!/\.[jt]sx$/.test(id))
|
|
74
|
+
return null;
|
|
75
|
+
const s = new MagicString(code);
|
|
76
|
+
if (!transform(code, id, opts?.ssr ?? false, s))
|
|
77
|
+
return null;
|
|
78
|
+
return {
|
|
79
|
+
code: s.toString(),
|
|
80
|
+
map: s.generateMap({ includeContent: true, source: id })
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { unheadReactPlugin as default, unheadReactPlugin };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unhead/react",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.0-beta.
|
|
4
|
+
"version": "3.0.0-beta.5",
|
|
5
5
|
"description": "Full-stack <head> manager built for React.",
|
|
6
6
|
"author": "Harlan Wilton <harlan@harlanzw.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -29,6 +29,14 @@
|
|
|
29
29
|
"types": "./dist/client.d.ts",
|
|
30
30
|
"default": "./dist/client.mjs"
|
|
31
31
|
},
|
|
32
|
+
"./stream/server": {
|
|
33
|
+
"types": "./dist/stream/server.d.ts",
|
|
34
|
+
"default": "./dist/stream/server.mjs"
|
|
35
|
+
},
|
|
36
|
+
"./stream/client": {
|
|
37
|
+
"types": "./dist/stream/client.d.ts",
|
|
38
|
+
"default": "./dist/stream/client.mjs"
|
|
39
|
+
},
|
|
32
40
|
"./utils": {
|
|
33
41
|
"types": "./dist/utils.d.ts",
|
|
34
42
|
"default": "./dist/utils.mjs"
|
|
@@ -36,6 +44,10 @@
|
|
|
36
44
|
"./plugins": {
|
|
37
45
|
"types": "./dist/plugins.d.ts",
|
|
38
46
|
"default": "./dist/plugins.mjs"
|
|
47
|
+
},
|
|
48
|
+
"./stream/vite": {
|
|
49
|
+
"types": "./dist/stream/vite.d.ts",
|
|
50
|
+
"default": "./dist/stream/vite.mjs"
|
|
39
51
|
}
|
|
40
52
|
},
|
|
41
53
|
"main": "dist/index.mjs",
|
|
@@ -49,11 +61,20 @@
|
|
|
49
61
|
"client": [
|
|
50
62
|
"dist/client"
|
|
51
63
|
],
|
|
64
|
+
"stream/server": [
|
|
65
|
+
"dist/stream/server"
|
|
66
|
+
],
|
|
67
|
+
"stream/client": [
|
|
68
|
+
"dist/stream/client"
|
|
69
|
+
],
|
|
52
70
|
"plugins": [
|
|
53
71
|
"dist/plugins"
|
|
54
72
|
],
|
|
55
73
|
"utils": [
|
|
56
74
|
"dist/utils"
|
|
75
|
+
],
|
|
76
|
+
"stream/vite": [
|
|
77
|
+
"dist/stream/vite"
|
|
57
78
|
]
|
|
58
79
|
}
|
|
59
80
|
},
|
|
@@ -62,7 +83,13 @@
|
|
|
62
83
|
"dist"
|
|
63
84
|
],
|
|
64
85
|
"peerDependencies": {
|
|
65
|
-
"react": ">=18.3.1"
|
|
86
|
+
"react": ">=18.3.1",
|
|
87
|
+
"vite": ">=6"
|
|
88
|
+
},
|
|
89
|
+
"peerDependenciesMeta": {
|
|
90
|
+
"vite": {
|
|
91
|
+
"optional": true
|
|
92
|
+
}
|
|
66
93
|
},
|
|
67
94
|
"build": {
|
|
68
95
|
"external": [
|
|
@@ -70,14 +97,18 @@
|
|
|
70
97
|
]
|
|
71
98
|
},
|
|
72
99
|
"dependencies": {
|
|
73
|
-
"
|
|
100
|
+
"magic-string": "^0.30.21",
|
|
101
|
+
"mlly": "^1.8.0",
|
|
102
|
+
"oxc-parser": "^0.106.0",
|
|
103
|
+
"unhead": "3.0.0-beta.5"
|
|
74
104
|
},
|
|
75
105
|
"devDependencies": {
|
|
76
106
|
"@testing-library/react": "^16.3.1",
|
|
77
107
|
"@types/react": "^19.2.7",
|
|
78
108
|
"@types/react-dom": "^19.2.3",
|
|
79
109
|
"react": "^19.2.3",
|
|
80
|
-
"react-dom": "^19.2.3"
|
|
110
|
+
"react-dom": "^19.2.3",
|
|
111
|
+
"vite": "7.2.2"
|
|
81
112
|
},
|
|
82
113
|
"scripts": {
|
|
83
114
|
"build": "unbuild",
|