@@ -14,6 +14,7 @@ mod tests;
1414use crate :: compile_script:: { compile_script_setup_inline, TemplateParts } ;
1515use crate :: compile_template:: {
1616 compile_template_block, compile_template_block_vapor, extract_template_parts,
17+ extract_template_parts_full,
1718} ;
1819use crate :: rewrite_default:: rewrite_default;
1920use crate :: script:: ScriptCompileContext ;
@@ -49,11 +50,12 @@ pub fn compile_sfc(
4950 let has_scoped = descriptor. styles . iter ( ) . any ( |s| s. scoped ) ;
5051
5152 // Detect vapor mode from script attrs
52- let is_vapor = descriptor
53- . script_setup
54- . as_ref ( )
55- . map ( |s| s. attrs . contains_key ( "vapor" ) )
56- . unwrap_or ( false )
53+ let is_vapor = options. vapor
54+ || descriptor
55+ . script_setup
56+ . as_ref ( )
57+ . map ( |s| s. attrs . contains_key ( "vapor" ) )
58+ . unwrap_or ( false )
5759 || descriptor
5860 . script
5961 . as_ref ( )
@@ -90,29 +92,36 @@ pub fn compile_sfc(
9092 // Case 1: Template only - just output render function
9193 if !has_script && !has_script_setup && has_template {
9294 let template = descriptor. template . as_ref ( ) . unwrap ( ) ;
93- // Enable hoisting for template-only SFCs (hoisted consts go at module level)
94- let mut template_opts = options. template . clone ( ) ;
95- let mut dom_opts = template_opts. compiler_options . take ( ) . unwrap_or_default ( ) ;
96- dom_opts. hoist_static = true ;
97- template_opts. compiler_options = Some ( dom_opts) ;
98- // Don't pass scope IDs to template compiler - scoped CSS is handled by
99- // runtime __scopeId and CSS transformation, not by adding attributes
100- // to template elements during compilation.
101- let template_result = compile_template_block (
102- template,
103- & template_opts,
104- & scope_id,
105- false ,
106- is_ts,
107- None ,
108- None ,
109- ) ;
95+ let template_result = if is_vapor {
96+ compile_template_block_vapor ( template, & scope_id, has_scoped, None )
97+ } else {
98+ // Enable hoisting for template-only SFCs (hoisted consts go at module level)
99+ let mut template_opts = options. template . clone ( ) ;
100+ let mut dom_opts = template_opts. compiler_options . take ( ) . unwrap_or_default ( ) ;
101+ dom_opts. hoist_static = true ;
102+ template_opts. compiler_options = Some ( dom_opts) ;
103+ // Don't pass scope IDs to template compiler - scoped CSS is handled by
104+ // runtime __scopeId and CSS transformation, not by adding attributes
105+ // to template elements during compilation.
106+ compile_template_block (
107+ template,
108+ & template_opts,
109+ & scope_id,
110+ false ,
111+ is_ts,
112+ None ,
113+ None ,
114+ )
115+ } ;
110116
111117 match template_result {
112118 Ok ( template_code) => {
113- // Template-only SFC: output just the render function with export.
114- // The template compiler already generates `export function render(...)`.
115119 code = template_code;
120+ if is_vapor {
121+ code. push_str ( "const _sfc_main = { __vapor: true }\n " ) ;
122+ code. push_str ( "_sfc_main.render = render\n " ) ;
123+ code. push_str ( "export default _sfc_main\n " ) ;
124+ }
116125 }
117126 Err ( e) => errors. push ( e) ,
118127 }
@@ -158,22 +167,26 @@ pub fn compile_sfc(
158167 // Compile template if present
159168 if has_template {
160169 let template = descriptor. template . as_ref ( ) . unwrap ( ) ;
161- let mut template_opts = options. template . clone ( ) ;
162- let mut dom_opts = template_opts. compiler_options . take ( ) . unwrap_or_default ( ) ;
163- dom_opts. hoist_static = true ;
164- template_opts. compiler_options = Some ( dom_opts) ;
165-
166- // Don't pass scope IDs to template compiler - scoped CSS is handled by
167- // runtime __scopeId and CSS transformation.
168- let template_result = compile_template_block (
169- template,
170- & template_opts,
171- & scope_id,
172- false ,
173- is_ts,
174- None , // No bindings for normal scripts
175- None , // No Croquis for normal scripts
176- ) ;
170+ let template_result = if is_vapor {
171+ compile_template_block_vapor ( template, & scope_id, has_scoped, None )
172+ } else {
173+ let mut template_opts = options. template . clone ( ) ;
174+ let mut dom_opts = template_opts. compiler_options . take ( ) . unwrap_or_default ( ) ;
175+ dom_opts. hoist_static = true ;
176+ template_opts. compiler_options = Some ( dom_opts) ;
177+
178+ // Don't pass scope IDs to template compiler - scoped CSS is handled by
179+ // runtime __scopeId and CSS transformation.
180+ compile_template_block (
181+ template,
182+ & template_opts,
183+ & scope_id,
184+ false ,
185+ is_ts,
186+ None , // No bindings for normal scripts
187+ None , // No Croquis for normal scripts
188+ )
189+ } ;
177190
178191 match template_result {
179192 Ok ( template_code) => {
@@ -187,6 +200,9 @@ pub fn compile_sfc(
187200 code. push ( '\n' ) ;
188201
189202 // Export the component with render attached
203+ if is_vapor {
204+ code. push_str ( "_sfc_main.__vapor = true\n " ) ;
205+ }
190206 code. push_str ( "_sfc_main.render = render\n " ) ;
191207 code. push_str ( "export default _sfc_main\n " ) ;
192208 }
@@ -200,6 +216,9 @@ pub fn compile_sfc(
200216 } else {
201217 // No template - just output rewritten script and export
202218 code. push_str ( & final_script) ;
219+ if is_vapor {
220+ code. push_str ( "\n _sfc_main.__vapor = true" ) ;
221+ }
203222 code. push_str ( "\n export default _sfc_main\n " ) ;
204223 }
205224
@@ -314,7 +333,10 @@ pub fn compile_sfc(
314333 let template_result = if let Some ( template) = & descriptor. template {
315334 if is_vapor {
316335 Some ( compile_template_block_vapor (
317- template, & scope_id, has_scoped,
336+ template,
337+ & scope_id,
338+ has_scoped,
339+ Some ( & script_bindings) ,
318340 ) )
319341 } else {
320342 // Don't pass scope IDs to template compiler - scoped CSS is handled by
@@ -334,23 +356,39 @@ pub fn compile_sfc(
334356 } ;
335357
336358 // Extract template parts for inline mode (imports, hoisted, preamble, render_body)
337- let ( template_imports, template_hoisted, template_preamble, render_body) =
359+ let ( template_imports, template_hoisted, template_render_fn , template_preamble, render_body) =
338360 match & template_result {
339- Some ( Ok ( template_code) ) => extract_template_parts ( template_code) ,
361+ Some ( Ok ( template_code) ) => {
362+ if is_vapor {
363+ let ( imports, hoisted, render_fn) = extract_template_parts_full ( template_code) ;
364+ (
365+ imports,
366+ hoisted,
367+ render_fn,
368+ String :: default ( ) ,
369+ String :: default ( ) ,
370+ )
371+ } else {
372+ let ( imports, hoisted, preamble, body) = extract_template_parts ( template_code) ;
373+ ( imports, hoisted, String :: default ( ) , preamble, body)
374+ }
375+ }
340376 Some ( Err ( e) ) => {
341377 errors. push ( e. clone ( ) ) ;
342378 (
343379 String :: default ( ) ,
344380 String :: default ( ) ,
345381 String :: default ( ) ,
346382 String :: default ( ) ,
383+ String :: default ( ) ,
347384 )
348385 }
349386 None => (
350387 String :: default ( ) ,
351388 String :: default ( ) ,
352389 String :: default ( ) ,
353390 String :: default ( ) ,
391+ String :: default ( ) ,
354392 ) ,
355393 } ;
356394
@@ -370,11 +408,14 @@ pub fn compile_sfc(
370408 & component_name,
371409 is_ts,
372410 source_is_ts,
411+ is_vapor,
373412 TemplateParts {
374413 imports : & template_imports,
375414 hoisted : & template_hoisted,
415+ render_fn : & template_render_fn,
376416 preamble : & template_preamble,
377417 render_body : & render_body,
418+ render_is_block : is_vapor,
378419 } ,
379420 normal_script_content. as_deref ( ) ,
380421 & descriptor. css_vars ,
0 commit comments