@useavalon/vue 0.1.2 → 0.1.3
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 +42 -42
- package/client/hmr-adapter.ts +226 -0
- package/client/hydration.ts +124 -124
- package/client/index.ts +7 -7
- package/mod.ts +59 -59
- package/package.json +19 -5
- package/server/css-extractor.ts +175 -175
- package/server/renderer.ts +116 -116
- package/types.ts +80 -80
- package/vitest.config.ts +0 -13
package/server/renderer.ts
CHANGED
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vue Server Renderer
|
|
3
|
-
*
|
|
4
|
-
* Provides server-side rendering capabilities for Vue components.
|
|
5
|
-
* Uses Vue's official SSR API with proper hydration support.
|
|
6
|
-
*
|
|
7
|
-
* Migrated from src/islands/renderers/vue-renderer.ts
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { createSSRApp } from 'vue';
|
|
11
|
-
import { renderToString as vueRenderToString } from 'vue/server-renderer';
|
|
12
|
-
import type { RenderParams, RenderResult } from '@useavalon/core/types';
|
|
13
|
-
import { extractCSS, generateScopeId, applyScopeToHTML } from './css-extractor.ts';
|
|
14
|
-
import { toImportSpecifier } from '@useavalon/core/utils';
|
|
15
|
-
import { resolveIslandPath } from '@useavalon/avalon/islands/framework-detection';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Render a Vue component to HTML string with SSR
|
|
19
|
-
*
|
|
20
|
-
* Creates a Vue SSR app instance and renders it to string.
|
|
21
|
-
* Extracts and applies scoped CSS from the component.
|
|
22
|
-
*
|
|
23
|
-
* Based on Vue.js SSR documentation and Astro's Vue integration:
|
|
24
|
-
* - Creates proper SSR app with createSSRApp
|
|
25
|
-
* - Wraps SSR HTML in a div with data-server-rendered="true"
|
|
26
|
-
* - Uses consistent container structure for client hydration
|
|
27
|
-
*
|
|
28
|
-
* @param params - Render parameters including component, props, and source path
|
|
29
|
-
* @returns Render result with HTML, CSS, and hydration data
|
|
30
|
-
*/
|
|
31
|
-
export async function render(params: RenderParams): Promise<RenderResult> {
|
|
32
|
-
const { component: _component, props = {}, src, condition = 'on:client', ssrOnly = false } = params;
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
const VueComponent = await loadComponent(src);
|
|
36
|
-
|
|
37
|
-
const app = createSSRApp(VueComponent as any, props);
|
|
38
|
-
const ssrHtml = await vueRenderToString(app);
|
|
39
|
-
|
|
40
|
-
let componentCSS = '';
|
|
41
|
-
let scopeId = '';
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
scopeId = generateScopeId(src);
|
|
45
|
-
componentCSS = await extractCSS(src, { scopeId });
|
|
46
|
-
} catch {
|
|
47
|
-
// CSS extraction failed, continue without CSS
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
let finalHtml = ssrHtml;
|
|
51
|
-
if (componentCSS) {
|
|
52
|
-
finalHtml = applyScopeToHTML(ssrHtml, scopeId);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
html: finalHtml,
|
|
57
|
-
css: componentCSS || undefined,
|
|
58
|
-
scopeId: scopeId || undefined,
|
|
59
|
-
hydrationData: { src, props, framework: 'vue', condition, ssrOnly },
|
|
60
|
-
};
|
|
61
|
-
} catch (error) {
|
|
62
|
-
throw new Error(`Vue SSR rendering failed: ${error}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Load a Vue component module
|
|
68
|
-
*
|
|
69
|
-
* Handles both development (via Vite) and production (pre-built) scenarios.
|
|
70
|
-
*
|
|
71
|
-
* @param src - Component source path
|
|
72
|
-
* @returns Vue component module
|
|
73
|
-
*/
|
|
74
|
-
async function loadComponent(src: string) {
|
|
75
|
-
const isDev = process.env.NODE_ENV !== 'production';
|
|
76
|
-
|
|
77
|
-
if (isDev && (globalThis as any).__viteDevServer) {
|
|
78
|
-
// Development: use Vite's SSR module loading
|
|
79
|
-
|
|
80
|
-
const viteServer = (globalThis as any).__viteDevServer;
|
|
81
|
-
const resolvedPath = await resolveIslandPath(src);
|
|
82
|
-
const module = await viteServer.ssrLoadModule(resolvedPath);
|
|
83
|
-
return module.default || module;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Production: load from build output
|
|
87
|
-
const ssrPath = src.replace('/islands/', '/dist/ssr/islands/').replace('.vue', '.js');
|
|
88
|
-
|
|
89
|
-
const module = await import(
|
|
90
|
-
/* @vite-ignore */
|
|
91
|
-
toImportSpecifier(ssrPath)
|
|
92
|
-
);
|
|
93
|
-
return module.default || module;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Get component metadata for debugging
|
|
98
|
-
*
|
|
99
|
-
* @param component - Vue component
|
|
100
|
-
* @returns Component metadata object
|
|
101
|
-
*/
|
|
102
|
-
export function getComponentMetadata(component: unknown) {
|
|
103
|
-
if (typeof component === 'object' && component !== null) {
|
|
104
|
-
return {
|
|
105
|
-
name: (component as { name?: string }).name || 'Anonymous',
|
|
106
|
-
type: 'component',
|
|
107
|
-
hasSetup: 'setup' in component,
|
|
108
|
-
hasTemplate: 'template' in component,
|
|
109
|
-
hasRender: 'render' in component,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return {
|
|
114
|
-
type: typeof component,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Vue Server Renderer
|
|
3
|
+
*
|
|
4
|
+
* Provides server-side rendering capabilities for Vue components.
|
|
5
|
+
* Uses Vue's official SSR API with proper hydration support.
|
|
6
|
+
*
|
|
7
|
+
* Migrated from src/islands/renderers/vue-renderer.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createSSRApp } from 'vue';
|
|
11
|
+
import { renderToString as vueRenderToString } from 'vue/server-renderer';
|
|
12
|
+
import type { RenderParams, RenderResult } from '@useavalon/core/types';
|
|
13
|
+
import { extractCSS, generateScopeId, applyScopeToHTML } from './css-extractor.ts';
|
|
14
|
+
import { toImportSpecifier } from '@useavalon/core/utils';
|
|
15
|
+
import { resolveIslandPath } from '@useavalon/avalon/islands/framework-detection';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Render a Vue component to HTML string with SSR
|
|
19
|
+
*
|
|
20
|
+
* Creates a Vue SSR app instance and renders it to string.
|
|
21
|
+
* Extracts and applies scoped CSS from the component.
|
|
22
|
+
*
|
|
23
|
+
* Based on Vue.js SSR documentation and Astro's Vue integration:
|
|
24
|
+
* - Creates proper SSR app with createSSRApp
|
|
25
|
+
* - Wraps SSR HTML in a div with data-server-rendered="true"
|
|
26
|
+
* - Uses consistent container structure for client hydration
|
|
27
|
+
*
|
|
28
|
+
* @param params - Render parameters including component, props, and source path
|
|
29
|
+
* @returns Render result with HTML, CSS, and hydration data
|
|
30
|
+
*/
|
|
31
|
+
export async function render(params: RenderParams): Promise<RenderResult> {
|
|
32
|
+
const { component: _component, props = {}, src, condition = 'on:client', ssrOnly = false } = params;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const VueComponent = await loadComponent(src);
|
|
36
|
+
|
|
37
|
+
const app = createSSRApp(VueComponent as any, props);
|
|
38
|
+
const ssrHtml = await vueRenderToString(app);
|
|
39
|
+
|
|
40
|
+
let componentCSS = '';
|
|
41
|
+
let scopeId = '';
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
scopeId = generateScopeId(src);
|
|
45
|
+
componentCSS = await extractCSS(src, { scopeId });
|
|
46
|
+
} catch {
|
|
47
|
+
// CSS extraction failed, continue without CSS
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let finalHtml = ssrHtml;
|
|
51
|
+
if (componentCSS) {
|
|
52
|
+
finalHtml = applyScopeToHTML(ssrHtml, scopeId);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
html: finalHtml,
|
|
57
|
+
css: componentCSS || undefined,
|
|
58
|
+
scopeId: scopeId || undefined,
|
|
59
|
+
hydrationData: { src, props, framework: 'vue', condition, ssrOnly },
|
|
60
|
+
};
|
|
61
|
+
} catch (error) {
|
|
62
|
+
throw new Error(`Vue SSR rendering failed: ${error}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Load a Vue component module
|
|
68
|
+
*
|
|
69
|
+
* Handles both development (via Vite) and production (pre-built) scenarios.
|
|
70
|
+
*
|
|
71
|
+
* @param src - Component source path
|
|
72
|
+
* @returns Vue component module
|
|
73
|
+
*/
|
|
74
|
+
async function loadComponent(src: string) {
|
|
75
|
+
const isDev = process.env.NODE_ENV !== 'production';
|
|
76
|
+
|
|
77
|
+
if (isDev && (globalThis as any).__viteDevServer) {
|
|
78
|
+
// Development: use Vite's SSR module loading
|
|
79
|
+
|
|
80
|
+
const viteServer = (globalThis as any).__viteDevServer;
|
|
81
|
+
const resolvedPath = await resolveIslandPath(src);
|
|
82
|
+
const module = await viteServer.ssrLoadModule(resolvedPath);
|
|
83
|
+
return module.default || module;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Production: load from build output
|
|
87
|
+
const ssrPath = src.replace('/islands/', '/dist/ssr/islands/').replace('.vue', '.js');
|
|
88
|
+
|
|
89
|
+
const module = await import(
|
|
90
|
+
/* @vite-ignore */
|
|
91
|
+
toImportSpecifier(ssrPath)
|
|
92
|
+
);
|
|
93
|
+
return module.default || module;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get component metadata for debugging
|
|
98
|
+
*
|
|
99
|
+
* @param component - Vue component
|
|
100
|
+
* @returns Component metadata object
|
|
101
|
+
*/
|
|
102
|
+
export function getComponentMetadata(component: unknown) {
|
|
103
|
+
if (typeof component === 'object' && component !== null) {
|
|
104
|
+
return {
|
|
105
|
+
name: (component as { name?: string }).name || 'Anonymous',
|
|
106
|
+
type: 'component',
|
|
107
|
+
hasSetup: 'setup' in component,
|
|
108
|
+
hasTemplate: 'template' in component,
|
|
109
|
+
hasRender: 'render' in component,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
type: typeof component,
|
|
115
|
+
};
|
|
116
|
+
}
|
package/types.ts
CHANGED
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vue Integration Types
|
|
3
|
-
*
|
|
4
|
-
* Type definitions specific to the Vue integration package.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { RenderParams, RenderResult } from '@useavalon/core/types';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Vue-specific render parameters
|
|
11
|
-
*/
|
|
12
|
-
export interface VueRenderParams extends RenderParams {
|
|
13
|
-
/**
|
|
14
|
-
* Vue app context for SSR
|
|
15
|
-
*/
|
|
16
|
-
context?: Map<string, unknown>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Vue-specific render result with CSS extraction
|
|
21
|
-
*/
|
|
22
|
-
export interface VueRenderResult extends RenderResult {
|
|
23
|
-
/**
|
|
24
|
-
* Extracted CSS from Vue SFC <style> blocks
|
|
25
|
-
*/
|
|
26
|
-
css?: string;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Head content (e.g., meta tags, title)
|
|
30
|
-
*/
|
|
31
|
-
head?: string;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Scope ID for scoped styles
|
|
35
|
-
*/
|
|
36
|
-
scopeId?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Vue component module structure
|
|
41
|
-
*/
|
|
42
|
-
export interface VueComponentModule {
|
|
43
|
-
default?: unknown;
|
|
44
|
-
[key: string]: unknown;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* CSS extraction options
|
|
49
|
-
*/
|
|
50
|
-
export interface CSSExtractionOptions {
|
|
51
|
-
/**
|
|
52
|
-
* Whether to apply scoping to CSS
|
|
53
|
-
*/
|
|
54
|
-
scoped?: boolean;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Custom scope ID (generated if not provided)
|
|
58
|
-
*/
|
|
59
|
-
scopeId?: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Style block metadata from Vue SFC
|
|
64
|
-
*/
|
|
65
|
-
export interface StyleBlock {
|
|
66
|
-
/**
|
|
67
|
-
* CSS content
|
|
68
|
-
*/
|
|
69
|
-
content: string;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Whether the style is scoped
|
|
73
|
-
*/
|
|
74
|
-
scoped: boolean;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Style attributes (e.g., lang, scoped)
|
|
78
|
-
*/
|
|
79
|
-
attributes: string;
|
|
80
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Vue Integration Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions specific to the Vue integration package.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { RenderParams, RenderResult } from '@useavalon/core/types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Vue-specific render parameters
|
|
11
|
+
*/
|
|
12
|
+
export interface VueRenderParams extends RenderParams {
|
|
13
|
+
/**
|
|
14
|
+
* Vue app context for SSR
|
|
15
|
+
*/
|
|
16
|
+
context?: Map<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Vue-specific render result with CSS extraction
|
|
21
|
+
*/
|
|
22
|
+
export interface VueRenderResult extends RenderResult {
|
|
23
|
+
/**
|
|
24
|
+
* Extracted CSS from Vue SFC <style> blocks
|
|
25
|
+
*/
|
|
26
|
+
css?: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Head content (e.g., meta tags, title)
|
|
30
|
+
*/
|
|
31
|
+
head?: string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Scope ID for scoped styles
|
|
35
|
+
*/
|
|
36
|
+
scopeId?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Vue component module structure
|
|
41
|
+
*/
|
|
42
|
+
export interface VueComponentModule {
|
|
43
|
+
default?: unknown;
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* CSS extraction options
|
|
49
|
+
*/
|
|
50
|
+
export interface CSSExtractionOptions {
|
|
51
|
+
/**
|
|
52
|
+
* Whether to apply scoping to CSS
|
|
53
|
+
*/
|
|
54
|
+
scoped?: boolean;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Custom scope ID (generated if not provided)
|
|
58
|
+
*/
|
|
59
|
+
scopeId?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Style block metadata from Vue SFC
|
|
64
|
+
*/
|
|
65
|
+
export interface StyleBlock {
|
|
66
|
+
/**
|
|
67
|
+
* CSS content
|
|
68
|
+
*/
|
|
69
|
+
content: string;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Whether the style is scoped
|
|
73
|
+
*/
|
|
74
|
+
scoped: boolean;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Style attributes (e.g., lang, scoped)
|
|
78
|
+
*/
|
|
79
|
+
attributes: string;
|
|
80
|
+
}
|
package/vitest.config.ts
DELETED