├── .npmrc ├── README.md ├── .github ├── FUNDING.yml └── workflows │ ├── release-pr.yml │ └── unit-test.yml ├── packages ├── compiler-rs │ ├── rustfmt.toml │ ├── tests │ │ ├── mod.rs │ │ ├── transform │ │ │ ├── mod.rs │ │ │ ├── snapshots │ │ │ │ ├── r#mod__transform__ssr__ssr_export.snap │ │ │ │ ├── r#mod__transform__ssr__ssr_export_default.snap │ │ │ │ ├── r#mod__transform__hmr__export.snap │ │ │ │ ├── r#mod__transform__hmr__export_default_with_function_declaration.snap │ │ │ │ ├── r#mod__transform__hmr__export_default_with_identifier.snap │ │ │ │ ├── r#mod__transform__hmr__export_default.snap │ │ │ │ ├── r#mod__transform__hmr__exports_with_define_component.snap │ │ │ │ ├── r#mod__transform__interop__basic.snap │ │ │ │ └── r#mod__transform__hmr__exports.snap │ │ │ ├── interop.rs │ │ │ └── ssr.rs │ │ └── vapor │ │ │ ├── snapshots │ │ │ ├── r#mod__vapor__transform_element__component_.snap │ │ │ ├── r#mod__vapor__transform_element__component_dynamic_props.snap │ │ │ ├── r#mod__vapor__transform_children__fragment.snap │ │ │ ├── r#mod__vapor__transform_text__consecutive_text.snap │ │ │ ├── r#mod__vapor__transform_children__comments.snap │ │ │ ├── r#mod__vapor__transform_text__on_consecutive_text.snap │ │ │ ├── r#mod__vapor__v_once__inside_v_once.snap │ │ │ ├── r#mod__vapor__transform_text__interpolation.snap │ │ │ ├── r#mod__vapor__transform_text__text_like.snap │ │ │ ├── r#mod__vapor__transform_element__props_children.snap │ │ │ ├── r#mod__vapor__transform_element__static_props.snap │ │ │ ├── r#mod__vapor__transform_element__component_fragment_should_not_mark_as_single_root.snap │ │ │ ├── r#mod__vapor__transform_text__static_template.snap │ │ │ ├── r#mod__vapor__v_bind__no_expression.snap │ │ │ ├── r#mod__vapor__v_once__as_root_node.snap │ │ │ ├── r#mod__vapor__v_show__basic.snap │ │ │ ├── r#mod__vapor__transform_element__component_generate_single_root_component.snap │ │ │ ├── r#mod__vapor__v_on__namespace_event_with_component.snap │ │ │ ├── r#mod__vapor__transform_element__component_resolve_namespaced_component.snap │ │ │ ├── r#mod__vapor__transform_element__component_event_with_once_modifier.snap │ │ │ ├── r#mod__vapor__transform_text__escapes_raw_static_text_when_generating_the_template_string.snap │ │ │ ├── r#mod__vapor__v_bind__attr_modifier_with_no_expression.snap │ │ │ ├── r#mod__vapor__v_bind__camel_modifier_with_no_expression.snap │ │ │ ├── r#mod__vapor__v_bind__prop_modifier_with_no_expression.snap │ │ │ ├── r#mod__vapor__v_slot__quote_slot_name.snap │ │ │ ├── r#mod__vapor__transform_element__component_props_merging_class.snap │ │ │ ├── r#mod__vapor__v_on__basic.snap │ │ │ ├── r#mod__vapor__v_model__basic.snap │ │ │ ├── r#mod__vapor__transform_element__component_dynamic_props_before_static_prop.snap │ │ │ ├── r#mod__vapor__transform_element__component_props_merging_style.snap │ │ │ ├── r#mod__vapor__v_bind__basic.snap │ │ │ ├── r#mod__vapor__v_on__should_delegate_event.snap │ │ │ ├── r#mod__vapor__custom_directive__basic.snap │ │ │ ├── r#mod__vapor__transform_element__component_static_props.snap │ │ │ ├── r#mod__vapor__v_slot__nested_component_slot.snap │ │ │ ├── r#mod__vapor__transform_element__props_merging_style.snap │ │ │ ├── r#mod__vapor__v_bind__attr_modifier.snap │ │ │ ├── r#mod__vapor__v_bind__camel_modifier.snap │ │ │ ├── r#mod__vapor__v_once__with_v_if.snap │ │ │ ├── r#mod__vapor__v_model__component.snap │ │ │ ├── r#mod__vapor__v_model__component_with_arguments.snap │ │ │ ├── r#mod__vapor__v_on__expression_with_type.snap │ │ │ ├── r#mod__vapor__transform_element__component_dynamic_props_after_static_prop.snap │ │ │ ├── r#mod__vapor__transform_element__component_v_on.snap │ │ │ ├── r#mod__vapor__v_bind__prop_modifier.snap │ │ │ ├── r#mod__vapor__v_if__component.snap │ │ │ ├── r#mod__vapor__v_model__modifiers_lazy.snap │ │ │ ├── r#mod__vapor__v_model__modifiers_trim.snap │ │ │ ├── r#mod__vapor__v_once__with_v_for.snap │ │ │ ├── r#mod__vapor__custom_directive__binding_value.snap │ │ │ ├── r#mod__vapor__v_model__modifiers_number.snap │ │ │ ├── r#mod__vapor__v_once__on_nested_plain_element.snap │ │ │ ├── r#mod__vapor__v_html__should_convert_v_html_to_inner_html.snap │ │ │ ├── r#mod__vapor__v_model__should_support_input_text.snap │ │ │ ├── r#mod__vapor__v_model__should_support_select.snap │ │ │ ├── r#mod__vapor__v_model__should_support_textarea.snap │ │ │ ├── r#mod__vapor__v_model__should_support_input_radio.snap │ │ │ ├── r#mod__vapor__transform_element__v_on.snap │ │ │ ├── r#mod__vapor__v_model__component_with_dynamic_arguments.snap │ │ │ ├── r#mod__vapor__v_model__should_support_member_expression.snap │ │ │ ├── r#mod__vapor__transform_element__component_dynamic_props_between_static_prop.snap │ │ │ ├── r#mod__vapor__v_bind__number_value.snap │ │ │ ├── r#mod__vapor__v_model__should_support_input_checkbox.snap │ │ │ ├── r#mod__vapor__transform_element__dynamic_props.snap │ │ │ ├── r#mod__vapor__transform_element__props_merging_class.snap │ │ │ ├── r#mod__vapor__transform_element__component_generate_multi_root_component.snap │ │ │ ├── r#mod__vapor__v_slots__basic.snap │ │ │ ├── r#mod__vapor__v_slot__on_component_default_slot.snap │ │ │ ├── r#mod__vapor__custom_directive__dynamic_argument.snap │ │ │ ├── r#mod__vapor__custom_directive__static_parameters.snap │ │ │ ├── r#mod__vapor__transform_children__basic.snap │ │ │ ├── r#mod__vapor__v_on__should_transform_click_middle.snap │ │ │ ├── r#mod__vapor__transform_template_ref__static_ref.snap │ │ │ ├── r#mod__vapor__v_on__should_transform_click_right.snap │ │ │ ├── r#mod__vapor__v_slot__implicit_default_slot.snap │ │ │ ├── r#mod__vapor__custom_directive__modifiers.snap │ │ │ ├── r#mod__vapor__transform_element__dynamic_props_after_static_prop.snap │ │ │ ├── r#mod__vapor__transform_element__dynamic_props_before_static_prop.snap │ │ │ ├── r#mod__vapor__v_slot__dynamic_slots_name.snap │ │ │ ├── r#mod__vapor__v_if__v_if_v_else.snap │ │ │ ├── r#mod__vapor__v_on__should_not_error_if_no_expression_but_has_modifier.snap │ │ │ ├── r#mod__vapor__v_on__should_not_wrap_keys_guard_if_no_key_modifier_is_present.snap │ │ │ ├── r#mod__vapor__v_on__should_wrap_keys_guard_for_static_key_event_with_left_or_right_modifiers.snap │ │ │ ├── r#mod__vapor__custom_directive__modifiers_with_binding.snap │ │ │ ├── r#mod__vapor__v_on__should_support_multiple_modifiers_and_event_options.snap │ │ │ ├── r#mod__vapor__custom_directive__static_argument_and_modifiers.snap │ │ │ ├── r#mod__vapor__v_once__with_v_if_else.snap │ │ │ ├── r#mod__vapor__v_slot__on_component_named_slot.snap │ │ │ ├── r#mod__vapor__transform_element__component_v_for_should_not_mark_as_single_root.snap │ │ │ ├── r#mod__vapor__v_if__dedupe_same_template.snap │ │ │ ├── r#mod__vapor__v_on__should_wrap_keys_guard_for_keyboard_events_or_dynamic_events.snap │ │ │ ├── r#mod__vapor__transform_element__component_import_resolve_component.snap │ │ │ ├── r#mod__vapor__transform_element__component_with_fallback.snap │ │ │ ├── r#mod__vapor__v_once__on_component.snap │ │ │ ├── r#mod__vapor__v_if__basic.snap │ │ │ ├── r#mod__vapor__v_if__v_if_v_if_else.snap │ │ │ ├── r#mod__vapor__v_text__should_convert_v_text_to_set_text.snap │ │ │ ├── r#mod__vapor__transform_element__invalid_html_nesting.snap │ │ │ ├── r#mod__vapor__v_once__basic.snap │ │ │ ├── r#mod__vapor__v_slots__nested.snap │ │ │ ├── r#mod__vapor__v_model__should_support_input_dynamic_type.snap │ │ │ ├── r#mod__vapor__v_slot__on_component_dynamically_named_slot.snap │ │ │ ├── r#mod__vapor__transform_element__dynamic_props_between_static_prop.snap │ │ │ ├── r#mod__vapor__transform_template_ref__dynamic_ref.snap │ │ │ ├── r#mod__vapor__v_model__component_should_generate_model_value_modifiers.snap │ │ │ ├── r#mod__vapor__transform_element__props_merging_event_handlers.snap │ │ │ ├── r#mod__vapor__v_model__should_support_dynamic_props.snap │ │ │ ├── r#mod__vapor__transform_children__efficient_find.snap │ │ │ ├── r#mod__vapor__v_if__v_on_with_v_if.snap │ │ │ ├── r#mod__vapor__v_on__should_support_multiple_events_and_modifiers_options.snap │ │ │ ├── r#mod__vapor__v_on__should_use_delegate_helper_when_have_multiple_events_of_same_name.snap │ │ │ ├── r#mod__vapor__v_for__selector_pattern3.snap │ │ │ ├── r#mod__vapor__v_for__key_only_binding_pattern.snap │ │ │ ├── r#mod__vapor__v_for__multi_effect.snap │ │ │ ├── r#mod__vapor__v_for__aliases_with_complex_expressions.snap │ │ │ ├── r#mod__vapor__v_model__component_with_arguments_should_generate_model_modifiers.snap │ │ │ ├── r#mod__vapor__v_slot__dynamic_slots_name_with_v_for.snap │ │ │ ├── r#mod__vapor__transform_children__jsx_component_in_jsx_expression_container.snap │ │ │ ├── r#mod__vapor__v_if__v_if_v_else_if_v_else.snap │ │ │ ├── r#mod__vapor__v_model__component_with_dynamic_arguments_with_v_for.snap │ │ │ ├── r#mod__vapor__v_for__object_de_structured_value.snap │ │ │ ├── r#mod__vapor__transform_template_ref__ref_v_if.snap │ │ │ ├── r#mod__vapor__v_for__array_de_structured_value.snap │ │ │ ├── r#mod__vapor__v_for__on_component.snap │ │ │ ├── r#mod__vapor__v_slot__named_slots_with_comment.snap │ │ │ ├── r#mod__vapor__v_for__object_value_key_and_index.snap │ │ │ ├── r#mod__vapor__transform_text__expression_logical.snap │ │ │ ├── r#mod__vapor__v_slot__nested_slots_scoping.snap │ │ │ ├── r#mod__vapor__transform_children__anchor_insertion_in_middle.snap │ │ │ ├── r#mod__vapor__v_for__on_template_with_single_component_child.snap │ │ │ ├── r#mod__vapor__v_for__selector_pattern4.snap │ │ │ ├── r#mod__vapor__v_for__selector_pattern2.snap │ │ │ ├── r#mod__vapor__v_for__basic.snap │ │ │ ├── r#mod__vapor__v_model__component_with_dynamic_arguments_should_generate_model_modifiers.snap │ │ │ ├── r#mod__vapor__v_slot__on_component_named_slot_multiple.snap │ │ │ ├── r#mod__vapor__v_if__comment_between_branches.snap │ │ │ ├── r#mod__vapor__v_slot__named_slots_with_implicit_default_slot.snap │ │ │ ├── r#mod__vapor__transform_template_ref__ref_v_for.snap │ │ │ ├── r#mod__vapor__v_model__should_support_member_expression_with_inline.snap │ │ │ ├── r#mod__vapor__v_for__fast_remove_flag.snap │ │ │ ├── r#mod__vapor__v_for__object_de_structured_value_with_rest.snap │ │ │ ├── r#mod__vapor__v_for__array_de_structured_value_with_rest.snap │ │ │ ├── r#mod__vapor__v_for__expression_object.snap │ │ │ ├── r#mod__vapor__v_if__template.snap │ │ │ ├── r#mod__vapor__v_for__selector_pattern1.snap │ │ │ ├── r#mod__vapor__transform_text__expression_conditional.snap │ │ │ ├── r#mod__vapor__v_once__execution_order.snap │ │ │ ├── r#mod__vapor__v_once__with_conditional_expression.snap │ │ │ ├── r#mod__vapor__v_bind__with_constant_value.snap │ │ │ ├── r#mod__vapor__transform_children__children_sibling_references.snap │ │ │ ├── r#mod__vapor__v_if__v_if_v_if_or_v_elses.snap │ │ │ ├── r#mod__vapor__v_for__nested_v_for.snap │ │ │ ├── r#mod__vapor__transform_children__efficient_traversal.snap │ │ │ ├── r#mod__vapor__transform_template_ref__function_ref.snap │ │ │ ├── r#mod__vapor__custom_directive__component.snap │ │ │ ├── r#mod__vapor__v_for__identifiers.snap │ │ │ ├── r#mod__vapor__transform_text__expression_map.snap │ │ │ ├── r#mod__vapor__transform_children__next_child_and_nthchild_should_be_above_the_set_insertion_state.snap │ │ │ └── r#mod__vapor__v_slot__dynamic_slots_name_with_v_if_and_v_else_if.snap │ │ │ ├── mod.rs │ │ │ ├── v_show.rs │ │ │ ├── transform_template_ref.rs │ │ │ ├── v_text.rs │ │ │ ├── v_html.rs │ │ │ ├── custom_directive.rs │ │ │ └── transform_text.rs │ ├── crates │ │ ├── vapor │ │ │ ├── src │ │ │ │ ├── ir.rs │ │ │ │ ├── lib.rs │ │ │ │ ├── transform │ │ │ │ │ ├── v_once.rs │ │ │ │ │ ├── v_show.rs │ │ │ │ │ └── v_html.rs │ │ │ │ └── generate │ │ │ │ │ ├── html.rs │ │ │ │ │ ├── dom.rs │ │ │ │ │ └── v_show.rs │ │ │ └── Cargo.toml │ │ └── common │ │ │ ├── src │ │ │ └── lib.rs │ │ │ └── Cargo.toml │ ├── browser.js │ ├── build.rs │ ├── .cargo │ │ └── config.toml │ ├── README.md │ ├── tsconfig.json │ ├── wasi-worker-browser.mjs │ ├── benches │ │ └── bench.rs │ ├── Cargo.toml │ ├── wasi-worker.mjs │ └── compiler-rs.wasi-browser.js ├── macros │ ├── src │ │ ├── api.ts │ │ ├── rollup.ts │ │ ├── rspack.ts │ │ ├── vite.ts │ │ ├── esbuild.ts │ │ ├── rolldown.ts │ │ ├── webpack.ts │ │ ├── index.ts │ │ ├── astro.ts │ │ ├── core │ │ │ ├── helper │ │ │ │ ├── index.ts │ │ │ │ └── with-defaults.ts │ │ │ ├── define-expose.ts │ │ │ ├── style.ts │ │ │ ├── define-component │ │ │ │ └── return.ts │ │ │ ├── define-slots.ts │ │ │ ├── define-model.ts │ │ │ └── utils.ts │ │ ├── nuxt.ts │ │ ├── volar.ts │ │ └── raw.ts │ ├── tsdown.config.ts │ └── tests │ │ ├── fixtures │ │ ├── define-slots.tsx │ │ ├── define-expose.tsx │ │ ├── define-model.tsx │ │ ├── define-style.tsx │ │ └── define-component.tsx │ │ └── fixtures.spec.ts ├── vue-jsx-vapor │ ├── src │ │ ├── api.ts │ │ ├── index.ts │ │ ├── vite.ts │ │ ├── esbuild.ts │ │ ├── rollup.ts │ │ ├── rspack.ts │ │ ├── webpack.ts │ │ ├── rolldown.ts │ │ ├── astro.ts │ │ ├── unplugin.ts │ │ ├── nuxt.ts │ │ ├── options.d.ts │ │ ├── core │ │ │ ├── index.ts │ │ │ ├── vue-jsx.ts │ │ │ └── ssr.ts │ │ └── volar.ts │ ├── tsdown.config.ts │ └── jsx-runtime │ │ ├── index.js │ │ ├── index.d.ts │ │ └── index.cjs ├── eslint │ ├── tsdown.config.ts │ ├── src │ │ ├── rules │ │ │ ├── define-style │ │ │ │ └── types.ts │ │ │ ├── index.ts │ │ │ └── jsx-sort-props │ │ │ │ └── types.ts │ │ └── index.ts │ ├── test │ │ ├── __snapshots__ │ │ │ ├── define-style.spec.ts.snap │ │ │ └── jsx-sort-props.spec.ts.snap │ │ └── define-style.spec.ts │ └── package.json └── runtime │ ├── tsdown.config.ts │ ├── src │ ├── index.ts │ ├── vdom.ts │ ├── block.ts │ └── helpers.ts │ └── package.json ├── docs ├── netlify.toml ├── vite.config.ts ├── public │ └── logo.svg ├── package.json ├── zh │ ├── features │ │ └── use-ref.md │ ├── index.md │ └── introduction │ │ ├── eslint.md │ │ └── getting-started.md ├── .vitepress │ └── theme │ │ └── index.ts ├── features │ └── use-ref.md └── index.md ├── .prettierrc.json ├── .stackblitzrc ├── playground ├── src │ ├── Comp.vue │ ├── count.tsx │ ├── once.tsx │ ├── show.tsx │ ├── html.tsx │ ├── model.tsx │ ├── interop.tsx │ ├── element.tsx │ ├── slot.tsx │ ├── for.tsx │ ├── if.tsx │ └── App.tsx ├── vite.js ├── index.html ├── vite.config.ts ├── tsconfig.json ├── main.ts └── package.json ├── .vscode ├── settings.json └── launch.json ├── ts-macro.config.ts ├── benchmark └── package.json ├── tsdown.config.ts ├── tsconfig.json ├── vitest.config.ts ├── eslint.config.ts ├── pnpm-workspace.yaml ├── LICENSE └── package.json /.npmrc: -------------------------------------------------------------------------------- 1 | ignore-workspace-root-check=true 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ./packages/vue-jsx-vapor/README.md -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [zhiyuanzmj] 2 | -------------------------------------------------------------------------------- /packages/compiler-rs/rustfmt.toml: -------------------------------------------------------------------------------- 1 | tab_spaces = 2 2 | -------------------------------------------------------------------------------- /packages/macros/src/api.ts: -------------------------------------------------------------------------------- 1 | export * from './core' 2 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/api.ts: -------------------------------------------------------------------------------- 1 | export * from './core' 2 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod transform; 2 | mod vapor; 3 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from '@vue-jsx-vapor/runtime' 2 | -------------------------------------------------------------------------------- /packages/compiler-rs/crates/vapor/src/ir.rs: -------------------------------------------------------------------------------- 1 | pub mod component; 2 | pub mod index; 3 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/mod.rs: -------------------------------------------------------------------------------- 1 | mod hmr; 2 | mod interop; 3 | mod ssr; 4 | -------------------------------------------------------------------------------- /docs/netlify.toml: -------------------------------------------------------------------------------- 1 | [[rewrites]] 2 | from = "/*" 3 | to = "/:path.html" 4 | status = 200 -------------------------------------------------------------------------------- /packages/compiler-rs/browser.js: -------------------------------------------------------------------------------- 1 | export * from '@vue-jsx-vapor/compiler-rs-wasm32-wasi' 2 | -------------------------------------------------------------------------------- /packages/macros/src/rollup.ts: -------------------------------------------------------------------------------- 1 | import unplugin from '.' 2 | 3 | export default unplugin.rollup 4 | -------------------------------------------------------------------------------- /packages/macros/src/rspack.ts: -------------------------------------------------------------------------------- 1 | import unplugin from '.' 2 | 3 | export default unplugin.rspack 4 | -------------------------------------------------------------------------------- /packages/macros/src/vite.ts: -------------------------------------------------------------------------------- 1 | import unplugin from '.' 2 | 3 | export default unplugin.vite 4 | -------------------------------------------------------------------------------- /packages/macros/src/esbuild.ts: -------------------------------------------------------------------------------- 1 | import unplugin from '.' 2 | 3 | export default unplugin.esbuild 4 | -------------------------------------------------------------------------------- /packages/macros/src/rolldown.ts: -------------------------------------------------------------------------------- 1 | import unplugin from '.' 2 | 3 | export default unplugin.rolldown 4 | -------------------------------------------------------------------------------- /packages/macros/src/webpack.ts: -------------------------------------------------------------------------------- 1 | import unplugin from '.' 2 | 3 | export default unplugin.webpack 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /.stackblitzrc: -------------------------------------------------------------------------------- 1 | { 2 | "installDependencies": true, 3 | "startCommand": "npm run dev | npm run play" 4 | } -------------------------------------------------------------------------------- /packages/compiler-rs/crates/vapor/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod generate; 2 | pub mod ir; 3 | pub mod transform; 4 | -------------------------------------------------------------------------------- /packages/compiler-rs/build.rs: -------------------------------------------------------------------------------- 1 | extern crate napi_build; 2 | 3 | fn main() { 4 | napi_build::setup(); 5 | } 6 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/vite.ts: -------------------------------------------------------------------------------- 1 | import unplugin from './unplugin' 2 | 3 | export default unplugin.vite 4 | -------------------------------------------------------------------------------- /playground/src/Comp.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /packages/eslint/tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import { config } from '../../tsdown.config.ts' 2 | 3 | export default config() 4 | -------------------------------------------------------------------------------- /packages/macros/tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import { config } from '../../tsdown.config.ts' 2 | 3 | export default config() 4 | -------------------------------------------------------------------------------- /packages/runtime/tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import { config } from '../../tsdown.config.ts' 2 | 3 | export default config() 4 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/esbuild.ts: -------------------------------------------------------------------------------- 1 | import unplugin from './unplugin' 2 | 3 | export default unplugin.esbuild 4 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/rollup.ts: -------------------------------------------------------------------------------- 1 | import unplugin from './unplugin' 2 | 3 | export default unplugin.rollup 4 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/rspack.ts: -------------------------------------------------------------------------------- 1 | import unplugin from './unplugin' 2 | 3 | export default unplugin.rspack 4 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/webpack.ts: -------------------------------------------------------------------------------- 1 | import unplugin from './unplugin' 2 | 3 | export default unplugin.webpack 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": "explicit" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /playground/vite.js: -------------------------------------------------------------------------------- 1 | import { startVite } from 'vite-hyper-config' 2 | 3 | startVite(undefined, { 4 | resolve: { 5 | conditions: ['jsx-vapor-dev'], 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | 6 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import { config } from '../../tsdown.config.ts' 2 | 3 | export default config({ 4 | entry: ['./src/*.ts', '!./**.d.ts'], 5 | }) 6 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_dynamic_props.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | 6 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/rolldown.ts: -------------------------------------------------------------------------------- 1 | import { createRollupPlugin } from 'unplugin' 2 | import { unpluginFactory } from './unplugin' 3 | 4 | export default createRollupPlugin(unpluginFactory) 5 | -------------------------------------------------------------------------------- /packages/compiler-rs/crates/common/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod check; 2 | pub mod directive; 3 | pub mod dom; 4 | pub mod error; 5 | pub mod expression; 6 | pub mod options; 7 | pub mod text; 8 | pub mod walk; 9 | -------------------------------------------------------------------------------- /ts-macro.config.ts: -------------------------------------------------------------------------------- 1 | import vueJsxVapor from './packages/vue-jsx-vapor/src/volar' 2 | 3 | export default { 4 | plugins: [ 5 | vueJsxVapor({ 6 | macros: true, 7 | }), 8 | ], 9 | } 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-pc-windows-msvc] 2 | rustflags = ["-C", "target-feature=+crt-static"] 3 | 4 | [target.i686-pc-windows-msvc] 5 | rustflags = ["-C", "target-feature=+crt-static"] 6 | -------------------------------------------------------------------------------- /docs/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | 3 | export default defineConfig({ 4 | resolve: { 5 | conditions: ['jsx-vapor-dev'], 6 | }, 7 | optimizeDeps: { 8 | exclude: ['vitepress'], 9 | }, 10 | }) 11 | -------------------------------------------------------------------------------- /playground/src/count.tsx: -------------------------------------------------------------------------------- 1 | import { computed, defineVaporComponent } from 'vue' 2 | 3 | export default defineVaporComponent(({ value = '' }) => { 4 | defineExpose({ 5 | double: computed(() => +value * 2), 6 | }) 7 | return
{value}
8 | }) 9 | -------------------------------------------------------------------------------- /playground/src/once.tsx: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default () => { 4 | const count = ref(3) 5 | 6 | return ( 7 | <> 8 | 9 |
{count.value}
10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/README.md: -------------------------------------------------------------------------------- 1 | # @vue-jsx-vapor/compiler-rs 2 | 3 | [![NPM version](https://img.shields.io/npm/v/@vue-jsx-vapor/compiler-rs?color=a1b858&label=)](https://www.npmjs.com/package/@vue-jsx-vapor/compiler-rs) 4 | 5 | Rust version of @vue-jsx-vapor/compiler. 6 | -------------------------------------------------------------------------------- /packages/runtime/src/index.ts: -------------------------------------------------------------------------------- 1 | export { shallowRef as useRef } from 'vue' 2 | export * from './jsx' 3 | export * from './h' 4 | export * from './node' 5 | export * from './block' 6 | export * from './component' 7 | export * from './helpers' 8 | export * from './vue' 9 | export * from './vdom' 10 | -------------------------------------------------------------------------------- /playground/src/show.tsx: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default () => { 4 | const show = ref(false) 5 | 6 | return ( 7 | <> 8 | 9 | {String(show.value)} 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /playground/src/html.tsx: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default () => { 4 | const foo = ref('
foo
') 5 | 6 | return ( 7 | <> 8 | 9 | 10 |
11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /packages/eslint/src/rules/define-style/types.ts: -------------------------------------------------------------------------------- 1 | export interface DefineStyleSchema0 { 2 | tabWidth?: number 3 | } 4 | 5 | export type DefineStyleRuleOptions = [DefineStyleSchema0?] 6 | 7 | export type RuleOptions = DefineStyleRuleOptions 8 | export type MessageIds = 'define-style' | 'define-style-syntax-error' 9 | -------------------------------------------------------------------------------- /packages/macros/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createUnplugin, type UnpluginInstance } from 'unplugin' 2 | import plugin from './raw' 3 | import type { Options } from './options' 4 | 5 | export * from './options' 6 | 7 | const unplugin: UnpluginInstance = createUnplugin(plugin) 8 | export default unplugin 9 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_children__fragment.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_children.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createNodes(() => foo); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_text__consecutive_text.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_text.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createNodes(() => msg); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/snapshots/r#mod__transform__ssr__ssr_export.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/transform/ssr.rs 3 | expression: code 4 | --- 5 | import { ssrRegisterHelper } from "/__vue-jsx-ssr-register-helper"; 6 | const __moduleId = "index.jsx"; 7 | export const foo = () => {}; 8 | ssrRegisterHelper(foo, __moduleId); 9 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_children__comments.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_children.rs 3 | expression: code 4 | --- 5 | import { template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | (() => { 8 | const n1 = t0(); 9 | return n1; 10 | })(); 11 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_text__on_consecutive_text.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_text.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createNodes("hello world"); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_once__inside_v_once.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_once.rs 3 | expression: code 4 | --- 5 | import { template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | return n0; 10 | })(); 11 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_text__interpolation.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_text.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createNodes(1, 2, () => a + b + c); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_text__text_like.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_text.rs 3 | expression: code 4 | --- 5 | import { template as _template } from "vue"; 6 | const t0 = _template("
2foo111
", true); 7 | (() => { 8 | const n0 = t0(); 9 | return n0; 10 | })(); 11 | -------------------------------------------------------------------------------- /docs/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/mod.rs: -------------------------------------------------------------------------------- 1 | mod custom_directive; 2 | mod transform_children; 3 | mod transform_element; 4 | mod transform_template_ref; 5 | mod transform_text; 6 | mod v_bind; 7 | mod v_for; 8 | mod v_html; 9 | mod v_if; 10 | mod v_model; 11 | mod v_on; 12 | mod v_once; 13 | mod v_show; 14 | mod v_slot; 15 | mod v_slots; 16 | mod v_text; 17 | -------------------------------------------------------------------------------- /playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "scripts": { 4 | "bench": "node ./bench.js" 5 | }, 6 | "devDependencies": { 7 | "@babel/core": "catalog:", 8 | "@vue-jsx-vapor/babel": "2.6.8", 9 | "@vue-jsx-vapor/compiler-rs": "workspace:*", 10 | "@vue/babel-plugin-jsx": "^1.5.0", 11 | "tinybench": "^5.0.1" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__props_children.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | return n0; 10 | })(); 11 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__static_props.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | return n0; 10 | })(); 11 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/snapshots/r#mod__transform__ssr__ssr_export_default.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/transform/ssr.rs 3 | expression: code 4 | --- 5 | import { ssrRegisterHelper } from "/__vue-jsx-ssr-register-helper"; 6 | const __moduleId = "index.jsx"; 7 | const Comp = () => {}; 8 | export default Comp; 9 | ssrRegisterHelper(Comp, __moduleId); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_fragment_should_not_mark_as_single_root.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Comp); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_text__static_template.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_text.rs 3 | expression: code 4 | --- 5 | import { template as _template } from "vue"; 6 | const t0 = _template("
hello
", true); 7 | (() => { 8 | const n0 = t0(); 9 | return n0; 10 | })(); 11 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_bind__no_expression.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_bind.rs 3 | expression: code 4 | --- 5 | import { setProp as _setProp, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _setProp(n0, "id", true); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_once__as_root_node.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_once.rs 3 | expression: code 4 | --- 5 | import { setProp as _setProp, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _setProp(n0, "id", foo); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_show__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_show.rs 3 | expression: code 4 | --- 5 | import { applyVShow as _applyVShow, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyVShow(n0, () => foo); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_generate_single_root_component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Comp, null, null, true); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__namespace_event_with_component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Comp, { "onUpdate:modelValue": () => () => {} }, null, true); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_resolve_namespaced_component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Foo.Example, null, null, true); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/macros/src/astro.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from './options' 2 | import unplugin from '.' 3 | 4 | export default (options: Options) => ({ 5 | name: 'vue-jsx-vapor', 6 | hooks: { 7 | 'astro:config:setup': (astro: any) => { 8 | astro.config.vite.plugins ||= [] 9 | astro.config.vite.plugins.push(unplugin.vite(options)) 10 | }, 11 | }, 12 | }) 13 | -------------------------------------------------------------------------------- /packages/macros/tests/fixtures/define-slots.tsx: -------------------------------------------------------------------------------- 1 | export const Comp = () => { 2 | const slots = defineSlots<{ 3 | default: () => any 4 | }>() 5 | return
{slots.default?.()}
6 | } 7 | 8 | export default function () { 9 | const slots = defineSlots({ 10 | default: () =>
default
, 11 | }) 12 | return
{slots.default?.()}
13 | } 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_event_with_once_modifier.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Foo, { onFooOnce: () => bar }, null, true); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_text__escapes_raw_static_text_when_generating_the_template_string.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_text.rs 3 | expression: code 4 | --- 5 | import { template as _template } from "vue"; 6 | const t0 = _template("<script>", true); 7 | (() => { 8 | const n0 = t0(); 9 | return n0; 10 | })(); 11 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_bind__attr_modifier_with_no_expression.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_bind.rs 3 | expression: code 4 | --- 5 | import { setAttr as _setAttr, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _setAttr(n0, "foo-bar", true); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_bind__camel_modifier_with_no_expression.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_bind.rs 3 | expression: code 4 | --- 5 | import { setAttr as _setAttr, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _setAttr(n0, "foo-bar", true); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_bind__prop_modifier_with_no_expression.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_bind.rs 3 | expression: code 4 | --- 5 | import { setProp as _setProp, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _setProp(n0, "fooBar", true); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__quote_slot_name.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n3 = _createComponent(Comp, null, { "nav-bar-title-before": () => { 8 | return null; 9 | } }, true); 10 | return n3; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/runtime/src/vdom.ts: -------------------------------------------------------------------------------- 1 | import { getCurrentInstance } from 'vue' 2 | 3 | const cacheMap = new WeakMap() 4 | 5 | export function useVdomCache() { 6 | const i = getCurrentInstance() 7 | if (i) { 8 | !cacheMap.has(i) && cacheMap.set(i, []) 9 | const caches = cacheMap.get(i) 10 | return (caches[caches.length] = []) 11 | } else { 12 | return [] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/macros/src/core/helper/index.ts: -------------------------------------------------------------------------------- 1 | export const helperPrefix = '/vue-jsx-vapor/macros' as const 2 | 3 | export const useModelHelperId = `${helperPrefix}/use-model` 4 | export { default as useModelHelperCode } from './use-model?raw' 5 | 6 | export const withDefaultsHelperId = `${helperPrefix}/with-defaults` as const 7 | export { default as withDefaultsHelperCode } from './with-defaults?raw' 8 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/astro.ts: -------------------------------------------------------------------------------- 1 | import unplugin from './unplugin' 2 | import type { Options } from './options' 3 | 4 | export default (options: Options) => ({ 5 | name: 'vue-jsx-vapor', 6 | hooks: { 7 | 'astro:config:setup': (astro: any) => { 8 | astro.config.vite.plugins ||= [] 9 | astro.config.vite.plugins.push(unplugin.vite(options)) 10 | }, 11 | }, 12 | }) 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_props_merging_class.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Foo, { class: () => ["foo", { bar: isBar }] }, null, true); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | _delegateEvents("click"); 6 | import { delegateEvents as _delegateEvents, template as _template } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | n0.$evtclick = handleClick; 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyTextModel as _applyTextModel, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyTextModel(n0, () => model, (_value) => model = _value); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_dynamic_props_before_static_prop.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Foo, { $: [() => obj, { id: () => "foo" }] }, null, true); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_props_merging_style.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Foo, { style: () => ["color: green", { color: "red" }] }, null, true); 8 | return n0; 9 | })(); 10 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_bind__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_bind.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setProp as _setProp, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setProp(n0, "id", id)); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__should_delegate_event.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | _delegateEvents("click"); 6 | import { delegateEvents as _delegateEvents, template as _template } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | n0.$evtclick = test; 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__custom_directive__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/custom_directive.rs 3 | expression: code 4 | --- 5 | import { template as _template, withVaporDirectives as _withVaporDirectives } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _withVaporDirectives(n0, [[vExample]]); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_static_props.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Foo, { 8 | id: () => "foo", 9 | class: () => "bar" 10 | }, null, true); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__nested_component_slot.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n1 = _createComponent(A, null, { default: () => { 8 | const n0 = _createComponent(B); 9 | return n0; 10 | } }, true); 11 | return n1; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/macros/tests/fixtures/define-expose.tsx: -------------------------------------------------------------------------------- 1 | export function Comp() { 2 | defineExpose({ 3 | foo: 1, 4 | }) 5 | return
6 | } 7 | 8 | export const Comp1 = function (props: any) { 9 | defineExpose({ 10 | foo: props.foo, 11 | }) 12 | return
13 | } 14 | 15 | export const Comp2 = ({ foo }: any) => { 16 | defineExpose({ 17 | foo, 18 | }) 19 | return
20 | } 21 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__props_merging_style.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { setStyle as _setStyle, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _setStyle(n0, ["color: green", { color: "red" }]); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_bind__attr_modifier.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_bind.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setAttr as _setAttr, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setAttr(n0, "foo-bar", id)); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_bind__camel_modifier.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_bind.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setProp as _setProp, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setProp(n0, "fooBar", id)); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_once__with_v_if.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_once.rs 3 | expression: code 4 | --- 5 | import { createIf as _createIf, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | (() => { 8 | const n0 = _createIf(() => expr, () => { 9 | const n2 = t0(); 10 | return n2; 11 | }, null, true); 12 | return n0; 13 | })(); 14 | -------------------------------------------------------------------------------- /playground/src/model.tsx: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | const Comp = () => { 4 | const model = defineModel() 5 | return 6 | } 7 | 8 | export default () => { 9 | const model = ref('model') 10 | 11 | return ( 12 | <> 13 | 14 | 15 | {model.value} 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Comp, { 8 | modelValue: () => foo, 9 | "onUpdate:modelValue": () => (_value) => foo = _value 10 | }, null, true); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__component_with_arguments.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Comp, { 8 | bar: () => foo, 9 | "onUpdate:bar": () => (_value) => foo = _value 10 | }, null, true); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__expression_with_type.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | _delegateEvents("click"); 6 | import { delegateEvents as _delegateEvents, template as _template } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | n0.$evtclick = handleClick as any; 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_dynamic_props_after_static_prop.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Foo, { 8 | id: () => "foo", 9 | $: [() => obj] 10 | }, null, true); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_v_on.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { toHandlers as _toHandlers } from "vue"; 7 | (() => { 8 | const n0 = _createComponent(Foo, { $: [() => _toHandlers(obj)] }, null, true); 9 | return n0; 10 | })(); 11 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_bind__prop_modifier.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_bind.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setDOMProp(n0, "fooBar", id)); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_if__component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_if.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { createIf as _createIf } from "vue"; 7 | (() => { 8 | const n0 = _createIf(() => foo, () => { 9 | const n2 = _createComponent(Comp); 10 | return n2; 11 | }); 12 | return n0; 13 | })(); 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__modifiers_lazy.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyTextModel as _applyTextModel, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyTextModel(n0, () => model, (_value) => model = _value, { lazy: true }); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__modifiers_trim.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyTextModel as _applyTextModel, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyTextModel(n0, () => model, (_value) => model = _value, { trim: true }); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_once__with_v_for.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_once.rs 3 | expression: code 4 | --- 5 | import { createFor as _createFor, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | (() => { 8 | const n0 = _createFor(() => list, (_for_item0) => { 9 | const n2 = t0(); 10 | return n2; 11 | }, void 0, 4); 12 | return n0; 13 | })(); 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__custom_directive__binding_value.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/custom_directive.rs 3 | expression: code 4 | --- 5 | import { template as _template, withVaporDirectives as _withVaporDirectives } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _withVaporDirectives(n0, [[vExample, () => msg]]); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__modifiers_number.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyTextModel as _applyTextModel, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyTextModel(n0, () => model, (_value) => model = _value, { number: true }); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_once__on_nested_plain_element.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_once.rs 3 | expression: code 4 | --- 5 | import { child as _child, setProp as _setProp, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n1 = t0(); 9 | const n0 = _child(n1); 10 | _setProp(n0, "id", foo); 11 | return n1; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "CommonJS", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "noUnusedLocals": true, 8 | "noUnusedParameters": true, 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true 11 | }, 12 | "include": ["."], 13 | "exclude": ["node_modules", "benchmark", "__test__"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/jsx-runtime/index.js: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'vue' 2 | import { h } from 'vue-jsx-vapor' 3 | 4 | function jsx(type, props, key) { 5 | const { children, 'v-slots': vSlots } = props 6 | delete props.children 7 | delete props['v-slots'] 8 | if (arguments.length > 2) props.key = key 9 | return h(type, props, vSlots || children) 10 | } 11 | 12 | export { Fragment, jsx, jsx as jsxDEV, jsx as jsxs } 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_html__should_convert_v_html_to_inner_html.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_html.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setHtml as _setHtml, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setHtml(n0, code.value)); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__should_support_input_text.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyTextModel as _applyTextModel, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyTextModel(n0, () => model, (_value) => model = _value); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__should_support_select.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applySelectModel as _applySelectModel, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applySelectModel(n0, () => model, (_value) => model = _value); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__should_support_textarea.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyTextModel as _applyTextModel, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyTextModel(n0, () => model, (_value) => model = _value); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__should_support_input_radio.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyRadioModel as _applyRadioModel, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyRadioModel(n0, () => model, (_value) => model = _value); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/crates/vapor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vapor" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | oxc_ast = { workspace = true } 8 | oxc_allocator = { workspace = true } 9 | oxc_span = { workspace = true } 10 | oxc_ast_visit = { workspace = true } 11 | oxc_traverse = { workspace = true } 12 | 13 | napi = { workspace = true } 14 | indexmap = { workspace = true } 15 | common = { workspace = true } 16 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__v_on.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setDynamicEvents as _setDynamicEvents, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setDynamicEvents(n0, obj)); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__component_with_dynamic_arguments.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Comp, { $: [() => ({ 8 | [arg]: foo, 9 | ["onUpdate:" + arg]: () => (_value) => foo = _value 10 | })] }, null, true); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__should_support_member_expression.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyTextModel as _applyTextModel, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyTextModel(n0, () => setupRef.child, (_value) => setupRef.child = _value); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_dynamic_props_between_static_prop.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Foo, { 8 | id: () => "foo", 9 | $: [() => obj, { class: () => "bar" }] 10 | }, null, true); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/snapshots/r#mod__transform__hmr__export.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/transform/hmr.rs 3 | expression: code 4 | --- 5 | export const foo = () => {}; 6 | foo.__hmrId = "3b6957b69bea9439"; 7 | __VUE_HMR_RUNTIME__.createRecord("3b6957b69bea9439", foo); 8 | if (import.meta.hot) import.meta.hot.accept((mod) => { 9 | __VUE_HMR_RUNTIME__[typeof mod.foo === "function" ? "rerender" : "reload"](mod.foo.__hmrId, mod.foo); 10 | }); 11 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_bind__number_value.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_bind.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n1 = t0(); 10 | const n3 = _createComponent(Comp, { depth: () => 0 }); 11 | return [n1, n3]; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__should_support_input_checkbox.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyCheckboxModel as _applyCheckboxModel, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyCheckboxModel(n0, () => model, (_value) => model = _value); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__dynamic_props.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setDynamicProps(n0, [obj], true)); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__props_merging_class.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setClass as _setClass, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setClass(n0, ["foo", { bar: isBar }])); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_generate_multi_root_component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { template as _template } from "vue"; 7 | const t0 = _template("123"); 8 | (() => { 9 | const n0 = _createComponent(Comp); 10 | const n1 = t0(); 11 | return [n0, n1]; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slots__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slots.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Comp, null, { $: [{ default: ({ foo }) => (() => { 8 | const n0 = _createNodes(() => foo + bar); 9 | return n0; 10 | })() }] }, true); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__on_component_default_slot.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n1 = _createComponent(Comp, null, { default: (scope) => { 8 | const n0 = _createNodes(() => scope.foo + bar); 9 | return n0; 10 | } }, true); 11 | return n1; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__custom_directive__dynamic_argument.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/custom_directive.rs 3 | expression: code 4 | --- 5 | import { template as _template, withVaporDirectives as _withVaporDirectives } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _withVaporDirectives(n0, [[ 10 | vExample, 11 | () => msg, 12 | foo 13 | ]]); 14 | return n0; 15 | })(); 16 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__custom_directive__static_parameters.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/custom_directive.rs 3 | expression: code 4 | --- 5 | import { template as _template, withVaporDirectives as _withVaporDirectives } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _withVaporDirectives(n0, [[ 10 | vExample, 11 | () => msg, 12 | "foo" 13 | ]]); 14 | return n0; 15 | })(); 16 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_children__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_children.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, template as _template } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | const x0 = _child(n0); 11 | _setNodes(x0, () => foo, " ", () => bar); 12 | return n0; 13 | })(); 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__should_transform_click_middle.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | _delegateEvents("mouseup"); 6 | import { delegateEvents as _delegateEvents, template as _template, withModifiers as _withModifiers } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | n0.$evtmouseup = _withModifiers(test, ["middle"]); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/unplugin.ts: -------------------------------------------------------------------------------- 1 | import { createUnplugin, type UnpluginFactory } from 'unplugin' 2 | import plugin from './raw' 3 | import type { Options } from './options' 4 | 5 | export type { Options } 6 | 7 | export const unpluginFactory: UnpluginFactory = ( 8 | options = {}, 9 | ) => { 10 | return plugin(options) 11 | } 12 | 13 | export const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory) 14 | 15 | export default unplugin 16 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_template_ref__static_ref.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_template_ref.rs 3 | expression: code 4 | --- 5 | import { createTemplateRefSetter as _createTemplateRefSetter, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const _setTemplateRef = _createTemplateRefSetter(); 9 | const n0 = t0(); 10 | _setTemplateRef(n0, "foo"); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__should_transform_click_right.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | _delegateEvents("contextmenu"); 6 | import { delegateEvents as _delegateEvents, template as _template, withModifiers as _withModifiers } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | n0.$evtcontextmenu = _withModifiers(test, ["right"]); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__implicit_default_slot.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n1 = _createComponent(Comp, null, { default: () => { 10 | const n0 = t0(); 11 | return n0; 12 | } }, true); 13 | return n1; 14 | })(); 15 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__custom_directive__modifiers.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/custom_directive.rs 3 | expression: code 4 | --- 5 | import { template as _template, withVaporDirectives as _withVaporDirectives } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _withVaporDirectives(n0, [[ 10 | vExample, 11 | () => msg, 12 | void 0, 13 | { bar: true } 14 | ]]); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__dynamic_props_after_static_prop.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setDynamicProps(n0, [{ id: "foo" }, obj], true)); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__dynamic_props_before_static_prop.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setDynamicProps(n0, [obj, { id: "foo" }], true)); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__dynamic_slots_name.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n4 = _createComponent(Comp, null, { $: [() => ({ 8 | name, 9 | fn: () => { 10 | const n1 = _createNodes(() => foo); 11 | return n1; 12 | } 13 | })] }, true); 14 | return n4; 15 | })(); 16 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_if__v_if_v_else.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_if.rs 3 | expression: code 4 | --- 5 | import { createIf as _createIf, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | const t1 = _template("

"); 8 | (() => { 9 | const n0 = _createIf(() => ok, () => { 10 | const n2 = t0(); 11 | return n2; 12 | }, () => { 13 | const n4 = t1(); 14 | return n4; 15 | }); 16 | return n0; 17 | })(); 18 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__should_not_error_if_no_expression_but_has_modifier.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | _delegateEvents("click"); 6 | import { delegateEvents as _delegateEvents, template as _template, withModifiers as _withModifiers } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | n0.$evtclick = _withModifiers(() => {}, ["prevent"]); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__should_not_wrap_keys_guard_if_no_key_modifier_is_present.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | _delegateEvents("keyup"); 6 | import { delegateEvents as _delegateEvents, template as _template, withModifiers as _withModifiers } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | n0.$evtkeyup = _withModifiers(test, ["exact"]); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__should_wrap_keys_guard_for_static_key_event_with_left_or_right_modifiers.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | _delegateEvents("keyup"); 6 | import { delegateEvents as _delegateEvents, template as _template, withKeys as _withKeys } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | n0.$evtkeyup = _withKeys(test, ["left"]); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/crates/common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "common" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [features] 7 | default = ["napi"] 8 | napi = [] 9 | 10 | [dependencies] 11 | oxc_span = { workspace = true } 12 | oxc_ast = { workspace = true } 13 | oxc_traverse = { workspace = true } 14 | oxc_allocator = { workspace = true } 15 | oxc_semantic = { workspace = true } 16 | 17 | napi-derive = { workspace = true } 18 | napi = { workspace = true } 19 | phf = { workspace = true } 20 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/snapshots/r#mod__transform__hmr__export_default_with_function_declaration.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/transform/hmr.rs 3 | expression: code 4 | --- 5 | export default function Comp() {} 6 | Comp.__hmrId = "52164bac249078a3"; 7 | __VUE_HMR_RUNTIME__.createRecord("52164bac249078a3", Comp); 8 | if (import.meta.hot) import.meta.hot.accept((mod) => { 9 | __VUE_HMR_RUNTIME__[typeof mod.default === "function" ? "rerender" : "reload"](mod.default.__hmrId, mod.default); 10 | }); 11 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__custom_directive__modifiers_with_binding.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/custom_directive.rs 3 | expression: code 4 | --- 5 | import { template as _template, withVaporDirectives as _withVaporDirectives } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _withVaporDirectives(n0, [[ 10 | vExample, 11 | void 0, 12 | void 0, 13 | { "foo-bar": true } 14 | ]]); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__should_support_multiple_modifiers_and_event_options.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | import { on as _on, template as _template, withModifiers as _withModifiers } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _on(n0, "click", _withModifiers(test, ["stop", "prevent"]), { 10 | capture: true, 11 | once: true 12 | }); 13 | return n0; 14 | })(); 15 | -------------------------------------------------------------------------------- /packages/macros/src/core/define-expose.ts: -------------------------------------------------------------------------------- 1 | import { importHelperFn, type MagicStringAST } from '@vue-macros/common' 2 | import type { CallExpression } from '@babel/types' 3 | 4 | export function transformDefineExpose( 5 | node: CallExpression, 6 | s: MagicStringAST, 7 | ): void { 8 | s.overwriteNode(node.callee, ';') 9 | s.appendRight( 10 | node.arguments[0]?.start || node.end! - 1, 11 | `${importHelperFn(s, 0, 'getCurrentInstance', undefined, 'vue-jsx-vapor')}().exposed = `, 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/snapshots/r#mod__transform__hmr__export_default_with_identifier.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/transform/hmr.rs 3 | expression: code 4 | --- 5 | const Comp = () => {}; 6 | export default Comp; 7 | Comp.__hmrId = "52164bac249078a3"; 8 | __VUE_HMR_RUNTIME__.createRecord("52164bac249078a3", Comp); 9 | if (import.meta.hot) import.meta.hot.accept((mod) => { 10 | __VUE_HMR_RUNTIME__[typeof mod.default === "function" ? "rerender" : "reload"](mod.default.__hmrId, mod.default); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__custom_directive__static_argument_and_modifiers.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/custom_directive.rs 3 | expression: code 4 | --- 5 | import { template as _template, withVaporDirectives as _withVaporDirectives } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _withVaporDirectives(n0, [[ 10 | vExample, 11 | () => msg, 12 | "foo", 13 | { bar: true } 14 | ]]); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_once__with_v_if_else.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_once.rs 3 | expression: code 4 | --- 5 | import { createIf as _createIf, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | const t1 = _template("

"); 8 | (() => { 9 | const n0 = _createIf(() => expr, () => { 10 | const n2 = t0(); 11 | return n2; 12 | }, () => { 13 | const n4 = t1(); 14 | return n4; 15 | }, true); 16 | return n0; 17 | })(); 18 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__on_component_named_slot.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n2 = _createComponent(Comp, null, { named: (_slotProps0) => { 8 | const n0 = _createNodes(() => ({ foo: _slotProps0.foo }), () => ({ foo: _slotProps0.foo })); 9 | return n0; 10 | } }, true); 11 | return n2; 12 | })(); 13 | -------------------------------------------------------------------------------- /playground/vite.config.ts: -------------------------------------------------------------------------------- 1 | import Vue from '@vitejs/plugin-vue' 2 | import DefineRender from '@vue-macros/define-render/vite' 3 | import { defineConfig } from 'vite' 4 | import Inspect from 'vite-plugin-inspect' 5 | import VueJsxVapor from 'vue-jsx-vapor/vite' 6 | 7 | export default defineConfig({ 8 | plugins: [ 9 | Vue(), 10 | VueJsxVapor({ 11 | // interop: true, 12 | macros: true, 13 | }), 14 | DefineRender({ 15 | vapor: true, 16 | }), 17 | Inspect(), 18 | ], 19 | }) 20 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_v_for_should_not_mark_as_single_root.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { createFor as _createFor } from "vue"; 7 | (() => { 8 | const n0 = _createFor(() => items, (_for_item0) => { 9 | const n2 = _createComponent(Comp); 10 | return n2; 11 | }, (item) => item, 2); 12 | return n0; 13 | })(); 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_if__dedupe_same_template.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_if.rs 3 | expression: code 4 | --- 5 | import { createIf as _createIf, template as _template } from "vue"; 6 | const t0 = _template("
hello
"); 7 | (() => { 8 | const n0 = _createIf(() => ok, () => { 9 | const n2 = t0(); 10 | return n2; 11 | }); 12 | const n3 = _createIf(() => ok, () => { 13 | const n5 = t0(); 14 | return n5; 15 | }); 16 | return [n0, n3]; 17 | })(); 18 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__should_wrap_keys_guard_for_keyboard_events_or_dynamic_events.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | import { on as _on, template as _template, withKeys as _withKeys, withModifiers as _withModifiers } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _on(n0, "keydown", _withKeys(_withModifiers(test, ["stop", "ctrl"]), ["a"]), { capture: true }); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/snapshots/r#mod__transform__hmr__export_default.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/transform/hmr.rs 3 | expression: code 4 | --- 5 | const __default__ = () => {}; 6 | export default __default__; 7 | __default__.__hmrId = "52164bac249078a3"; 8 | __VUE_HMR_RUNTIME__.createRecord("52164bac249078a3", __default__); 9 | if (import.meta.hot) import.meta.hot.accept((mod) => { 10 | __VUE_HMR_RUNTIME__[typeof mod.default === "function" ? "rerender" : "reload"](mod.default.__hmrId, mod.default); 11 | }); 12 | -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "preserve", 5 | "jsxImportSource": "vue-jsx-vapor", 6 | "lib": ["ESNext", "DOM"], 7 | "customConditions": ["jsx-vapor-dev"], 8 | "module": "ESNext", 9 | "moduleResolution": "bundler", 10 | "resolveJsonModule": true, 11 | "types": ["vite/client", "@vue-macros/define-render/macros-global.d.ts"], 12 | "strict": true, 13 | "strictNullChecks": true, 14 | "esModuleInterop": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_import_resolve_component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponentWithFallback as _createComponentWithFallback } from "vue-jsx-vapor"; 6 | import { resolveComponent as _resolveComponent } from "vue"; 7 | (() => { 8 | const _component_Foo = _resolveComponent("Foo"); 9 | const n0 = _createComponentWithFallback(_component_Foo, null, null, true); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__component_with_fallback.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { createComponentWithFallback as _createComponentWithFallback } from "vue-jsx-vapor"; 6 | import { resolveComponent as _resolveComponent } from "vue"; 7 | (() => { 8 | const _component_foo_bar = _resolveComponent("foo-bar"); 9 | const n0 = _createComponentWithFallback(_component_foo_bar, null, null, true); 10 | return n0; 11 | })(); 12 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_once__on_component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_once.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { setInsertionState as _setInsertionState, template as _template } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n1 = t0(); 10 | _setInsertionState(n1); 11 | const n0 = _createComponent(Comp, { id: () => foo }, null, null, true); 12 | return n1; 13 | })(); 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_if__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_if.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createIf as _createIf, template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n0 = _createIf(() => ok, () => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => msg); 13 | return n2; 14 | }); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_if__v_if_v_if_else.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_if.rs 3 | expression: code 4 | --- 5 | import { createIf as _createIf, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | const t1 = _template("

"); 8 | (() => { 9 | const n0 = _createIf(() => ok, () => { 10 | const n2 = t0(); 11 | return n2; 12 | }, () => _createIf(() => orNot, () => { 13 | const n4 = t1(); 14 | return n4; 15 | })); 16 | return n0; 17 | })(); 18 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_text__should_convert_v_text_to_set_text.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_text.rs 3 | expression: code 4 | --- 5 | import { child as _child, renderEffect as _renderEffect, setText as _setText, template as _template, toDisplayString as _toDisplayString } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | const x0 = _child(n0); 10 | _renderEffect(() => _setText(x0, _toDisplayString(str.value))); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/eslint/test/__snapshots__/define-style.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`define-style > basic 1`] = ` 4 | " 5 | defineStyle(\` 6 | .foo { 7 | color: red; 8 | } 9 | \`) 10 | " 11 | `; 12 | 13 | exports[`define-style > syntax error 1`] = ` 14 | " 15 | defineStyle(\` 16 | .foo { 17 | color: red 18 | background: blue; 19 | } 20 | \`) 21 | " 22 | `; 23 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__invalid_html_nesting.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { template as _template } from "vue"; 6 | const t0 = _template("
123
"); 7 | const t1 = _template("

"); 8 | const t2 = _template("
"); 9 | (() => { 10 | const n1 = t1(); 11 | const n0 = t0(); 12 | const n4 = t2(); 13 | const n3 = t2(); 14 | insert(n0, n1); 15 | insert(n3, n4); 16 | return [n1, n4]; 17 | })(); 18 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_once__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_once.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, next as _next, setClass as _setClass, template as _template } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n2 = t0(); 10 | const n0 = _child(n2); 11 | const n1 = _next(n0); 12 | _setNodes(n0, msg); 13 | _setClass(n1, clz); 14 | return n2; 15 | })(); 16 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slots__nested.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slots.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n5 = _createComponent(Comp, null, { default: (_slotProps0) => { 8 | const n2 = _createComponent(Comp, { bar: () => _slotProps0.bar }); 9 | const n3 = _createNodes(() => _slotProps0.bar); 10 | return [n2, n3]; 11 | } }, true); 12 | return n5; 13 | })(); 14 | -------------------------------------------------------------------------------- /playground/main.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createVaporApp, 3 | // vaporInteropPlugin, 4 | } from 'vue' 5 | 6 | const modules = import.meta.glob('./src/*.tsx') 7 | const mod = ( 8 | modules[`./src${location.pathname}.tsx`] || modules['./src/App.tsx'] 9 | )() 10 | 11 | mod.then(({ default: mod }) => { 12 | const app = createVaporApp(mod) 13 | // app.use(vaporInteropPlugin). 14 | .mount('#app') 15 | 16 | // @ts-expect-error 17 | globalThis.unmount = () => { 18 | // @ts-expect-error 19 | app.unmount() 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vitepress dev", 7 | "build": "vitepress build", 8 | "preview": "vitepress preview" 9 | }, 10 | "dependencies": { 11 | "@vue-jsx-vapor/eslint": "workspace:*", 12 | "vue": "catalog:", 13 | "vue-jsx-vapor": "workspace:*" 14 | }, 15 | "devDependencies": { 16 | "@shikijs/vitepress-twoslash": "^3.13.0", 17 | "@ts-macro/twoslash": "^0.0.5", 18 | "vitepress": "2.0.0-alpha.5" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__should_support_input_dynamic_type.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyDynamicModel as _applyDynamicModel, renderEffect as _renderEffect, setProp as _setProp, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyDynamicModel(n0, () => model, (_value) => model = _value); 10 | _renderEffect(() => _setProp(n0, "type", foo)); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__on_component_dynamically_named_slot.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n1 = _createComponent(Comp, null, { $: [() => ({ 8 | name: named, 9 | fn: (_slotProps0) => { 10 | const n0 = _createNodes(() => _slotProps0.foo + bar); 11 | return n0; 12 | } 13 | })] }, true); 14 | return n1; 15 | })(); 16 | -------------------------------------------------------------------------------- /packages/macros/tests/fixtures/define-model.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { defineVaporComponent, unref } from 'vue' 3 | 4 | const $ = unref 5 | 6 | export const Comp = defineVaporComponent(({ bar }: { bar: string }) => { 7 | const foo = defineModel('foo', { default: bar }) 8 | return
{foo.value}
9 | }) 10 | 11 | export default function () { 12 | const modelValue = $(defineModel()!) 13 | return ( 14 | 15 | {modelValue} 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__dynamic_props_between_static_prop.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | import { renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _renderEffect(() => _setDynamicProps(n0, [ 10 | { id: "foo" }, 11 | obj, 12 | { class: "bar" } 13 | ], true)); 14 | return n0; 15 | })(); 16 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_template_ref__dynamic_ref.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_template_ref.rs 3 | expression: code 4 | --- 5 | import { createTemplateRefSetter as _createTemplateRefSetter, renderEffect as _renderEffect, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const _setTemplateRef = _createTemplateRefSetter(); 9 | const n0 = t0(); 10 | let r0; 11 | _renderEffect(() => r0 = _setTemplateRef(n0, foo, r0)); 12 | return n0; 13 | })(); 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__component_should_generate_model_value_modifiers.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Comp, { 8 | modelValue: () => foo, 9 | "onUpdate:modelValue": () => (_value) => foo = _value, 10 | modelValueModifiers: () => ({ 11 | trim: true, 12 | "bar-baz": true 13 | }) 14 | }, null, true); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_element__props_merging_event_handlers.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_element.rs 3 | expression: code 4 | --- 5 | _delegateEvents("click"); 6 | import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template, withKeys as _withKeys } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | _delegate(n0, "click", _withKeys(a, ["foo"])); 11 | _delegate(n0, "click", _withKeys(b, ["bar"])); 12 | return n0; 13 | })(); 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__should_support_dynamic_props.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyDynamicModel as _applyDynamicModel, renderEffect as _renderEffect, setDynamicProps as _setDynamicProps, template as _template } from "vue"; 6 | const t0 = _template("", true); 7 | (() => { 8 | const n0 = t0(); 9 | _applyDynamicModel(n0, () => model, (_value) => model = _value); 10 | _renderEffect(() => _setDynamicProps(n0, [obj], true)); 11 | return n0; 12 | })(); 13 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_children__efficient_find.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_children.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, nthChild as _nthChild, template as _template } from "vue"; 7 | const t0 = _template("
x
x
", true); 8 | (() => { 9 | const n1 = t0(); 10 | const n0 = _nthChild(n1, 2); 11 | const x0 = _child(n0); 12 | _setNodes(x0, () => msg); 13 | return n1; 14 | })(); 15 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_if__v_on_with_v_if.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_if.rs 3 | expression: code 4 | --- 5 | import { createIf as _createIf, renderEffect as _renderEffect, setDynamicEvents as _setDynamicEvents, template as _template } from "vue"; 6 | const t0 = _template(""); 7 | (() => { 8 | const n0 = _createIf(() => true, () => { 9 | const n2 = t0(); 10 | _renderEffect(() => _setDynamicEvents(n2, { click: clickEvent })); 11 | return n2; 12 | }, null, true); 13 | return n0; 14 | })(); 15 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__should_support_multiple_events_and_modifiers_options.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | _delegateEvents("click", "keyup"); 6 | import { delegateEvents as _delegateEvents, template as _template, withKeys as _withKeys, withModifiers as _withModifiers } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | n0.$evtclick = _withModifiers(test, ["stop"]); 11 | n0.$evtkeyup = _withKeys(test, ["enter"]); 12 | return n0; 13 | })(); 14 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_on__should_use_delegate_helper_when_have_multiple_events_of_same_name.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_on.rs 3 | expression: code 4 | --- 5 | _delegateEvents("click"); 6 | import { delegate as _delegate, delegateEvents as _delegateEvents, template as _template, withModifiers as _withModifiers } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | _delegate(n0, "click", test); 11 | _delegate(n0, "click", _withModifiers(test, ["stop"])); 12 | return n0; 13 | })(); 14 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground", 3 | "version": "3.0.4", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "node vite.js --port 5174", 8 | "build": "node vite.js build" 9 | }, 10 | "devDependencies": { 11 | "@vitejs/plugin-vue": "^6.0.1", 12 | "@vue-macros/define-render": "catalog:", 13 | "vite": "catalog:", 14 | "vite-hyper-config": "^0.7.1", 15 | "vite-node": "^3.2.4", 16 | "vite-plugin-inspect": "^11.3.3", 17 | "vue": "catalog:", 18 | "vue-jsx-vapor": "workspace:*" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__selector_pattern3.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { createFor as _createFor, renderEffect as _renderEffect, setClass as _setClass, template as _template } from "vue"; 6 | const t0 = _template(""); 7 | (() => { 8 | const n0 = _createFor(() => rows, (_for_item0) => { 9 | const n2 = t0(); 10 | _renderEffect(() => _setClass(n2, _for_item0.value.label === _for_item0.value.id ? "danger" : "")); 11 | return n2; 12 | }, (row) => row.id); 13 | return n0; 14 | })(); 15 | -------------------------------------------------------------------------------- /packages/eslint/test/__snapshots__/jsx-sort-props.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`jsx-sort-props > basic 1`] = ` 4 | " 5 |
6 | " 7 | `; 8 | 9 | exports[`jsx-sort-props > reservedFirst 1`] = `""`; 10 | 11 | exports[`jsx-sort-props > reservedFirst and reservedLast 1`] = `" {}} v-slot:a={{ foo }} v-slot:b={{ foo }} />"`; 12 | 13 | exports[`jsx-sort-props > reservedLast 1`] = `" {}} v-slot={{ foo }} />"`; 14 | -------------------------------------------------------------------------------- /packages/macros/src/core/style.ts: -------------------------------------------------------------------------------- 1 | import { compileStyleAsync } from '@vue/compiler-sfc' 2 | import type { OptionsResolved } from '../options' 3 | 4 | export async function transformStyle( 5 | code: string, 6 | id: string, 7 | options: OptionsResolved, 8 | ): Promise { 9 | const query = new URLSearchParams(id.split('?')[1]) 10 | const result = await compileStyleAsync({ 11 | filename: id, 12 | id: `data-v-${query.get('scopeId')}`, 13 | isProd: options.isProduction, 14 | source: code, 15 | scoped: query.get('scoped') === 'true', 16 | }) 17 | 18 | return result.code 19 | } 20 | -------------------------------------------------------------------------------- /packages/macros/src/nuxt.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, addWebpackPlugin, defineNuxtModule } from '@nuxt/kit' 2 | import vite from './vite' 3 | import webpack from './webpack' 4 | import type { Options } from './options' 5 | import '@nuxt/schema' 6 | 7 | export interface ModuleOptions extends Options {} 8 | 9 | export default defineNuxtModule({ 10 | meta: { 11 | name: 'nuxt-vue-jsx-vapor', 12 | configKey: 'unpluginStarter', 13 | }, 14 | setup(options) { 15 | addVitePlugin(() => vite(options)) 16 | addWebpackPlugin(() => webpack(options)) 17 | 18 | // ... 19 | }, 20 | }) 21 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/nuxt.ts: -------------------------------------------------------------------------------- 1 | import { addVitePlugin, addWebpackPlugin, defineNuxtModule } from '@nuxt/kit' 2 | import vite from './vite' 3 | import webpack from './webpack' 4 | import type { Options } from './options' 5 | import '@nuxt/schema' 6 | 7 | export interface ModuleOptions extends Options {} 8 | 9 | export default defineNuxtModule({ 10 | meta: { 11 | name: 'nuxt-vue-jsx-vapor', 12 | configKey: 'jsxVapor', 13 | }, 14 | setup(options) { 15 | addVitePlugin(() => vite(options)) 16 | addWebpackPlugin(() => webpack(options)) 17 | 18 | // ... 19 | }, 20 | }) 21 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__key_only_binding_pattern.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, template as _template } from "vue"; 7 | const t0 = _template(" "); 8 | (() => { 9 | const n0 = _createFor(() => rows, (_for_item0) => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => _for_item0.value.id + _for_item0.value.id); 13 | return n2; 14 | }, (row) => row.id); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__multi_effect.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { createFor as _createFor, renderEffect as _renderEffect, setProp as _setProp, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | (() => { 8 | const n0 = _createFor(() => items, (_for_item0, _for_key0) => { 9 | const n2 = t0(); 10 | _renderEffect(() => { 11 | _setProp(n2, "item", _for_item0.value); 12 | _setProp(n2, "index", _for_key0.value); 13 | }); 14 | return n2; 15 | }); 16 | return n0; 17 | })(); 18 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__aliases_with_complex_expressions.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n0 = _createFor(() => list, (_for_item0) => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => _for_item0.value.foo + baz + _for_item0.value.baz[0]); 13 | return n2; 14 | }); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__component_with_arguments_should_generate_model_modifiers.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Comp, { 8 | foo: () => foo, 9 | "onUpdate:foo": () => (_value) => foo = _value, 10 | fooModifiers: () => ({ trim: true }), 11 | bar: () => bar, 12 | "onUpdate:bar": () => (_value) => bar = _value, 13 | barModifiers: () => ({ number: true }) 14 | }, null, true); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__dynamic_slots_name_with_v_for.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { createForSlots as _createForSlots } from "vue"; 7 | (() => { 8 | const n4 = _createComponent(Comp, null, { $: [() => _createForSlots(list, (item) => ({ 9 | name: item, 10 | fn: (_slotProps0) => { 11 | const n1 = _createNodes(() => _slotProps0.bar); 12 | return n1; 13 | } 14 | }))] }, true); 15 | return n4; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_children__jsx_component_in_jsx_expression_container.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_children.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { child as _child, template as _template } from "vue"; 7 | const t0 = _template("
", true); 8 | (() => { 9 | const n0 = t0(); 10 | const x0 = _child(n0); 11 | _setNodes(x0, () => (() => { 12 | const n0 = _createComponent(Comp, null, null, true); 13 | return n0; 14 | })()); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/macros/src/core/define-component/return.ts: -------------------------------------------------------------------------------- 1 | import { isFunctionalNode, type FunctionalNode } from '../utils' 2 | import type { MagicStringAST } from '@vue-macros/common' 3 | 4 | export function transformReturn(root: FunctionalNode, s: MagicStringAST): void { 5 | const node = 6 | root.body.type === 'BlockStatement' 7 | ? root.body.body.find((node) => node.type === 'ReturnStatement')?.argument 8 | : root.body 9 | if (!node || isFunctionalNode(node)) return 10 | 11 | s.appendRight( 12 | node.extra?.parenthesized ? (node.extra.parenStart as number) : node.start!, 13 | '() => ', 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /docs/zh/features/use-ref.md: -------------------------------------------------------------------------------- 1 | # useRef 2 | 3 | 自动为 `useRef` 推断类型。它是 `shallowRef` 的别名。 4 | 5 | ## 基本用法 6 | 7 | ```tsx twoslash 8 | import { defineVaporComponent } from 'vue' 9 | import { useRef } from 'vue-jsx-vapor' 10 | // 或者 11 | // import { shallowRef as useRef } from 'vue' 12 | 13 | export const Comp = () => { 14 | defineExpose({ 15 | foo: 1, 16 | }) 17 | 18 | return
19 | } 20 | 21 | export default defineVaporComponent(() => { 22 | const comp = useRef() 23 | comp.value?.foo 24 | // ^? 25 | 26 | return ( 27 |
28 | 29 |
30 | ) 31 | }) 32 | ``` 33 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_if__v_if_v_else_if_v_else.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_if.rs 3 | expression: code 4 | --- 5 | import { createIf as _createIf, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | const t1 = _template("

"); 8 | const t2 = _template("fine"); 9 | (() => { 10 | const n0 = _createIf(() => ok, () => { 11 | const n2 = t0(); 12 | return n2; 13 | }, () => _createIf(() => orNot, () => { 14 | const n4 = t1(); 15 | return n4; 16 | }, () => { 17 | const n7 = t2(); 18 | return n7; 19 | })); 20 | return n0; 21 | })(); 22 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__component_with_dynamic_arguments_with_v_for.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { createFor as _createFor } from "vue"; 7 | (() => { 8 | const n0 = _createFor(() => list, (_for_item0) => { 9 | const n2 = _createComponent(Comp, { $: [() => ({ 10 | [_for_item0.value.arg]: foo, 11 | ["onUpdate:" + _for_item0.value.arg]: () => (_value) => foo = _value 12 | })] }); 13 | return n2; 14 | }, void 0, 2); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/macros/src/core/define-slots.ts: -------------------------------------------------------------------------------- 1 | import { importHelperFn, type MagicStringAST } from '@vue-macros/common' 2 | import type { CallExpression } from '@babel/types' 3 | 4 | export function transformDefineSlots( 5 | node: CallExpression, 6 | s: MagicStringAST, 7 | ): void { 8 | s.overwrite( 9 | node.start!, 10 | (node.arguments[0]?.start && node.arguments[0].start - 1) || 11 | node.typeArguments?.end || 12 | node.callee.end!, 13 | `Object.assign`, 14 | ) 15 | const slots = `${importHelperFn(s, 0, 'useSlots')}()` 16 | s.appendLeft(node.end! - 1, `${node.arguments[0] ? ',' : '{}, '}${slots}`) 17 | } 18 | -------------------------------------------------------------------------------- /tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import process from 'node:process' 2 | import { defineConfig, type Options } from 'tsdown' 3 | import Raw from 'unplugin-raw/rolldown' 4 | 5 | export const config = (options: Options = {}) => 6 | defineConfig({ 7 | entry: ['./src/*.ts', '!./**.d.ts'], 8 | clean: true, 9 | format: ['cjs', 'esm'], 10 | watch: !!process.env.DEV, 11 | dts: !process.env.DEV, 12 | external: ['vue'], 13 | define: { 14 | __DEV__: 'true', 15 | }, 16 | plugins: [Raw()], 17 | outputOptions: { 18 | exports: 'named', 19 | }, 20 | ...options, 21 | }) 22 | 23 | export default config() 24 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__object_de_structured_value.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, template as _template } from "vue"; 7 | const t0 = _template(" "); 8 | (() => { 9 | const n0 = _createFor(() => items, (_for_item0) => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => _for_item0.value.id, () => _for_item0.value.value); 13 | return n2; 14 | }, ({ id, value }) => id); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/options.d.ts: -------------------------------------------------------------------------------- 1 | import type { CompilerOptions } from '@vue-jsx-vapor/compiler' 2 | import type { Options as MacrosOptions } from '@vue-jsx-vapor/macros' 3 | import type { FilterPattern } from 'unplugin-utils' 4 | 5 | export interface Options { 6 | // define your plugin options here 7 | include?: FilterPattern 8 | exclude?: FilterPattern 9 | interop?: boolean 10 | compile?: CompilerOptions 11 | sourceMap?: boolean 12 | /** @default true */ 13 | ref?: 14 | | { 15 | alias?: string[] 16 | } 17 | | boolean 18 | /** @default false */ 19 | macros?: MacrosOptions | boolean 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "preserve", 5 | "jsxImportSource": "vue-jsx-vapor", 6 | "lib": ["esnext", "DOM"], 7 | "useDefineForClassFields": false, 8 | "customConditions": ["jsx-vapor-dev"], 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "types": ["vite/client"], 13 | "allowImportingTsExtensions": true, 14 | "strict": true, 15 | "strictNullChecks": true, 16 | "noEmit": true, 17 | "esModuleInterop": true, 18 | "skipLibCheck": true 19 | }, 20 | "include": ["packages"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/core/index.ts: -------------------------------------------------------------------------------- 1 | import { transform } from '@vue-jsx-vapor/compiler-rs' 2 | import type { Options } from '../options' 3 | 4 | export type { Options } 5 | 6 | export function transformVueJsxVapor( 7 | code: string, 8 | id: string, 9 | options?: Options, 10 | needSourceMap = false, 11 | needHMR = false, 12 | ssr = false, 13 | ) { 14 | const params = new URLSearchParams(id) 15 | const vapor = params.get('vapor') 16 | return transform(code, { 17 | filename: id, 18 | sourceMap: needSourceMap, 19 | interop: vapor ? false : options?.interop, 20 | hmr: needHMR, 21 | ssr, 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /playground/src/interop.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, defineVaporComponent, ref } from 'vue' 2 | 3 | const VaporComp = defineVaporComponent( 4 | (props) => { 5 | return ( 6 |
7 | Vapor Component: 8 | {props.model} 9 |
10 | ) 11 | }, 12 | { props: ['model'] }, 13 | ) 14 | 15 | const Comp = (props) =>
Virtual Dom Component:{props.model}
16 | 17 | export default defineComponent(() => { 18 | const model = ref('') 19 | return () => [ 20 | , 21 | , 22 | , 23 | ] 24 | }) 25 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/interop.rs: -------------------------------------------------------------------------------- 1 | use compiler_rs::{TransformOptions, transform}; 2 | use insta::assert_snapshot; 3 | 4 | #[test] 5 | fn basic() { 6 | let code = transform( 7 | "const A = defineComponent(() => { 8 | defineVaporComponent(() => ) 9 | return () =>
10 | }) 11 | const B = defineVaporComponent(() => { 12 | const C = defineComponent(() =>
) 13 | const D = <>{foo}
14 | return
15 | })", 16 | Some(TransformOptions { 17 | interop: true, 18 | ..Default::default() 19 | }), 20 | ) 21 | .code; 22 | assert_snapshot!(code); 23 | } 24 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import TwoslashFloatingVue from '@shikijs/vitepress-twoslash/client' 2 | import DefaultTheme from 'vitepress/theme' 3 | // https://vitepress.dev/guide/custom-theme 4 | import { h } from 'vue' 5 | import type { Theme } from 'vitepress' 6 | import './style.css' 7 | import '@shikijs/vitepress-twoslash/style.css' 8 | 9 | export default { 10 | extends: DefaultTheme, 11 | Layout: () => { 12 | return h(DefaultTheme.Layout, null, { 13 | // https://vitepress.dev/guide/extending-default-theme#layout-slots 14 | }) 15 | }, 16 | enhanceApp({ app }) { 17 | app.use(TwoslashFloatingVue) 18 | }, 19 | } satisfies Theme 20 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_template_ref__ref_v_if.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_template_ref.rs 3 | expression: code 4 | --- 5 | import { createIf as _createIf, createTemplateRefSetter as _createTemplateRefSetter, renderEffect as _renderEffect, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | (() => { 8 | const _setTemplateRef = _createTemplateRefSetter(); 9 | const n0 = _createIf(() => true, () => { 10 | const n2 = t0(); 11 | let r2; 12 | _renderEffect(() => r2 = _setTemplateRef(n2, foo, r2)); 13 | return n2; 14 | }, null, true); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__array_de_structured_value.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n0 = _createFor(() => list, (_for_item0, _for_key0) => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => _for_item0.value[0] + _for_item0.value[1] + _for_key0.value); 13 | return n2; 14 | }, ([id, other], index) => id); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__on_component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { createFor as _createFor, template as _template } from "vue"; 7 | const t0 = _template(" "); 8 | (() => { 9 | const n0 = _createFor(() => list, (_for_item0) => { 10 | const n3 = _createComponent(Comp, null, { default: () => { 11 | const n2 = t0(); 12 | _setNodes(n2, () => _for_item0.value); 13 | return n2; 14 | } }); 15 | return n3; 16 | }, void 0, 2); 17 | return n0; 18 | })(); 19 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__named_slots_with_comment.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { template as _template } from "vue"; 7 | const t0 = _template("foo"); 8 | const t1 = _template(""); 9 | (() => { 10 | const n8 = _createComponent(Comp, null, { 11 | one: () => { 12 | const n3 = t0(); 13 | return n3; 14 | }, 15 | default: () => { 16 | const n5 = t0(); 17 | const n6 = t1(); 18 | return [n5, n6]; 19 | } 20 | }, true); 21 | return n8; 22 | })(); 23 | -------------------------------------------------------------------------------- /docs/features/use-ref.md: -------------------------------------------------------------------------------- 1 | # useRef 2 | 3 | Automatically infer type for `useRef`. It's an alias of `shallowRef`. 4 | 5 | ## Basic Usage 6 | 7 | ```tsx twoslash 8 | import { defineVaporComponent } from 'vue' 9 | import { useRef } from 'vue-jsx-vapor' 10 | // or 11 | // import { shallowRef as useRef } from 'vue' 12 | 13 | export const Comp = () => { 14 | defineExpose({ 15 | foo: 1 16 | }) 17 | 18 | return
19 | } 20 | 21 | export default defineVaporComponent(() => { 22 | const comp = useRef() 23 | comp.value?.foo 24 | // ^? 25 | 26 | return ( 27 |
28 | 29 |
30 | ) 31 | }) 32 | ``` 33 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__object_value_key_and_index.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, template as _template } from "vue"; 7 | const t0 = _template(" "); 8 | (() => { 9 | const n0 = _createFor(() => items, (_for_item0, _for_key0, _for_index0) => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => id, () => _for_item0.value, () => _for_index0.value); 13 | return n2; 14 | }, (value, key, index) => id); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_text__expression_logical.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_text.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes, createNodes as _createNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createIf as _createIf, template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n0 = _createIf(() => ok, () => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => msg); 13 | return n2; 14 | }, () => { 15 | const n4 = _createNodes(() => ok); 16 | return n4; 17 | }); 18 | return n0; 19 | })(); 20 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__nested_slots_scoping.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n11 = _createComponent(Comp, null, { default: (_slotProps0) => { 8 | const n5 = _createComponent(Inner, null, { default: (_slotProps1) => { 9 | const n3 = _createNodes(() => _slotProps0.foo + _slotProps1.bar + baz); 10 | return n3; 11 | } }); 12 | const n7 = _createNodes(() => _slotProps0.foo + bar + baz); 13 | return [n5, n7]; 14 | } }, true); 15 | return n11; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/macros/src/core/define-model.ts: -------------------------------------------------------------------------------- 1 | import { importHelperFn, type MagicStringAST } from '@vue-macros/common' 2 | import { useModelHelperId } from './helper' 3 | import type { CallExpression } from '@babel/types' 4 | 5 | export function transformDefineModel( 6 | node: CallExpression, 7 | propsName: string, 8 | s: MagicStringAST, 9 | ): void { 10 | s.overwriteNode( 11 | node.callee, 12 | importHelperFn(s, 0, 'useModel', undefined, useModelHelperId), 13 | ) 14 | s.appendRight( 15 | node.arguments[0]?.start || node.end! - 1, 16 | `${propsName}, ${ 17 | node.arguments[0]?.type !== 'StringLiteral' ? `'modelValue',` : '' 18 | }`, 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_children__anchor_insertion_in_middle.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_children.rs 3 | expression: code 4 | --- 5 | import { child as _child, createIf as _createIf, next as _next, setInsertionState as _setInsertionState, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | const t1 = _template("
", true); 8 | (() => { 9 | const n4 = t1(); 10 | const n3 = _next(_child(n4)); 11 | _setInsertionState(n4, n3); 12 | const n0 = _createIf(() => 1, () => { 13 | const n2 = t0(); 14 | return n2; 15 | }, null, true); 16 | return n4; 17 | })(); 18 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | resolve: { 5 | conditions: ['jsx-vapor-dev'], 6 | }, 7 | test: { 8 | include: ['./packages/**/*.spec.ts'], 9 | }, 10 | define: { 11 | __DEV__: true, 12 | __TEST__: true, 13 | __VERSION__: '"test"', 14 | __GLOBAL__: false, 15 | __ESM_BUNDLER__: true, 16 | __ESM_BROWSER__: false, 17 | __CJS__: true, 18 | __SSR__: true, 19 | __FEATURE_OPTIONS_API__: true, 20 | __FEATURE_SUSPENSE__: true, 21 | __FEATURE_PROD_DEVTOOLS__: false, 22 | __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__: false, 23 | __COMPAT__: true, 24 | }, 25 | }) 26 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__on_template_with_single_component_child.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes, createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { createFor as _createFor, template as _template } from "vue"; 7 | const t0 = _template(" "); 8 | (() => { 9 | const n0 = _createFor(() => list, (_for_item0) => { 10 | const n3 = _createComponent(Comp, null, { default: () => { 11 | const n2 = t0(); 12 | _setNodes(n2, () => _for_item0.value); 13 | return n2; 14 | } }); 15 | return n3; 16 | }, void 0, 2); 17 | return n0; 18 | })(); 19 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__selector_pattern4.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { createFor as _createFor, setClass as _setClass, template as _template } from "vue"; 6 | const t0 = _template(""); 7 | (() => { 8 | let _selector0_0; 9 | const n0 = _createFor(() => rows, (_for_item0) => { 10 | const n2 = t0(); 11 | _selector0_0(() => { 12 | _setClass(n2, { danger: _for_item0.value.id === selected }); 13 | }); 14 | return n2; 15 | }, (row) => row.id, void 0, ({ createSelector }) => { 16 | _selector0_0 = createSelector(() => selected); 17 | }); 18 | return n0; 19 | })(); 20 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__selector_pattern2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { createFor as _createFor, setClass as _setClass, template as _template } from "vue"; 6 | const t0 = _template(""); 7 | (() => { 8 | let _selector0_0; 9 | const n0 = _createFor(() => rows, (_for_item0) => { 10 | const n2 = t0(); 11 | _selector0_0(() => { 12 | _setClass(n2, selected === _for_item0.value.id ? "danger" : ""); 13 | }); 14 | return n2; 15 | }, (row) => row.id, void 0, ({ createSelector }) => { 16 | _selector0_0 = createSelector(() => selected); 17 | }); 18 | return n0; 19 | })(); 20 | -------------------------------------------------------------------------------- /.github/workflows/release-pr.yml: -------------------------------------------------------------------------------- 1 | name: Publish Any Commit 2 | on: [pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - name: Checkout Repo 10 | uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | 14 | - uses: pnpm/action-setup@v4.0.0 15 | 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: lts/* 20 | cache: pnpm 21 | 22 | - name: Install dependencies 23 | run: pnpm install 24 | 25 | - name: Build 26 | run: pnpm build 27 | 28 | - name: Publish 29 | run: pnpm dlx pkg-pr-new@0.0 publish 30 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | _delegateEvents("click"); 6 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 7 | import { child as _child, createFor as _createFor, delegateEvents as _delegateEvents, template as _template } from "vue"; 8 | const t0 = _template("
"); 9 | (() => { 10 | const n0 = _createFor(() => items, (_for_item0) => { 11 | const n2 = t0(); 12 | n2.$evtclick = () => remove(_for_item0.value); 13 | const x2 = _child(n2); 14 | _setNodes(x2, () => _for_item0.value); 15 | return n2; 16 | }, (item) => item.id); 17 | return n0; 18 | })(); 19 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__component_with_dynamic_arguments_should_generate_model_modifiers.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | (() => { 7 | const n0 = _createComponent(Comp, { $: [() => ({ 8 | [foo]: foo, 9 | ["onUpdate:" + foo]: () => (_value) => foo = _value, 10 | [foo + "Modifiers"]: () => ({ trim: true }) 11 | }), () => ({ 12 | [bar.value]: bar, 13 | ["onUpdate:" + bar.value]: () => (_value) => bar = _value, 14 | [bar.value + "Modifiers"]: () => ({ number: true }) 15 | })] }, null, true); 16 | return n0; 17 | })(); 18 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__on_component_named_slot_multiple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { template as _template } from "vue"; 7 | const t0 = _template("foo"); 8 | (() => { 9 | const n10 = _createComponent(Comp, null, { 10 | left: () => { 11 | const n1 = t0(); 12 | return n1; 13 | }, 14 | right: () => { 15 | const n6 = _createComponent(Comp, null, { left: () => { 16 | const n5 = t0(); 17 | return n5; 18 | } }); 19 | return n6; 20 | } 21 | }, true); 22 | return n10; 23 | })(); 24 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/ssr.rs: -------------------------------------------------------------------------------- 1 | use compiler_rs::{TransformOptions, transform}; 2 | use insta::assert_snapshot; 3 | 4 | #[test] 5 | pub fn ssr_export() { 6 | let code = transform( 7 | "export const foo = () => {}", 8 | Some(TransformOptions { 9 | ssr: true, 10 | ..Default::default() 11 | }), 12 | ) 13 | .code; 14 | assert_snapshot!(code); 15 | } 16 | 17 | #[test] 18 | pub fn ssr_export_default() { 19 | let code = transform( 20 | " 21 | const Comp = () => {} 22 | export default Comp 23 | ", 24 | Some(TransformOptions { 25 | ssr: true, 26 | ..Default::default() 27 | }), 28 | ) 29 | .code; 30 | assert_snapshot!(code); 31 | } 32 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_if__comment_between_branches.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_if.rs 3 | expression: code 4 | --- 5 | import { createIf as _createIf, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | const t1 = _template("

"); 8 | const t2 = _template("fine"); 9 | const t3 = _template("
text
"); 10 | (() => { 11 | const n1 = _createIf(() => ok, () => { 12 | const n3 = t0(); 13 | return n3; 14 | }, () => _createIf(() => orNot, () => { 15 | const n8 = t1(); 16 | return n8; 17 | }, () => { 18 | const n14 = t2(); 19 | return n14; 20 | })); 21 | const n18 = t3(); 22 | return [n1, n18]; 23 | })(); 24 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__named_slots_with_implicit_default_slot.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { template as _template } from "vue"; 7 | const t0 = _template("foo"); 8 | const t1 = _template("bar"); 9 | const t2 = _template(""); 10 | (() => { 11 | const n6 = _createComponent(Comp, null, { 12 | one: () => { 13 | const n1 = t0(); 14 | return n1; 15 | }, 16 | default: () => { 17 | const n3 = t1(); 18 | const n4 = t2(); 19 | return [n3, n4]; 20 | } 21 | }, true); 22 | return n6; 23 | })(); 24 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_template_ref__ref_v_for.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_template_ref.rs 3 | expression: code 4 | --- 5 | import { createFor as _createFor, createTemplateRefSetter as _createTemplateRefSetter, renderEffect as _renderEffect, template as _template } from "vue"; 6 | const t0 = _template("
"); 7 | (() => { 8 | const _setTemplateRef = _createTemplateRefSetter(); 9 | const n0 = _createFor(() => [ 10 | 1, 11 | 2, 12 | 3 13 | ], (_for_item0) => { 14 | const n2 = t0(); 15 | let r2; 16 | _renderEffect(() => r2 = _setTemplateRef(n2, foo, r2, true)); 17 | return n2; 18 | }, void 0, 4); 19 | return n0; 20 | })(); 21 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_model__should_support_member_expression_with_inline.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_model.rs 3 | expression: code 4 | --- 5 | import { applyTextModel as _applyTextModel, template as _template } from "vue"; 6 | const t0 = _template(""); 7 | (() => { 8 | const n0 = t0(); 9 | const n1 = t0(); 10 | const n2 = t0(); 11 | _applyTextModel(n0, () => setupRef.child, (_value) => setupRef.child = _value); 12 | _applyTextModel(n1, () => setupLet.child, (_value) => setupLet.child = _value); 13 | _applyTextModel(n2, () => setupMaybeRef.child, (_value) => setupMaybeRef.child = _value); 14 | return [ 15 | n0, 16 | n1, 17 | n2 18 | ]; 19 | })(); 20 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/jsx-runtime/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Fragment, type Block, type VNode } from 'vue' 2 | import type { h, NativeElements, ReservedProps } from 'vue-jsx-vapor' 3 | 4 | declare function jsx(type: any, props: any, key: any): ReturnType 5 | declare global { 6 | namespace JSX { 7 | type Element = VNode | Block | Element[] 8 | interface ElementAttributesProperty { 9 | $props 10 | } 11 | interface IntrinsicElements extends NativeElements { 12 | [name: string]: any 13 | } 14 | interface IntrinsicAttributes extends ReservedProps { 15 | class?: unknown 16 | style?: unknown 17 | } 18 | } 19 | } 20 | export { Fragment, jsx, jsx as jsxDEV, jsx as jsxs } 21 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Vitest - Debug Current Test File", 11 | "autoAttachChildProcesses": true, 12 | "skipFiles": ["/**", "**/node_modules/**"], 13 | "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs", 14 | "args": ["run", "${relativeFile}"], 15 | "smartStep": true, 16 | "console": "integratedTerminal" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__fast_remove_flag.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, setInsertionState as _setInsertionState, template as _template } from "vue"; 7 | const t0 = _template(" "); 8 | const t1 = _template("
", true); 9 | (() => { 10 | const n3 = t1(); 11 | _setInsertionState(n3); 12 | const n0 = _createFor(() => i, (_for_item0) => { 13 | const n2 = t0(); 14 | const x2 = _child(n2); 15 | _setNodes(x2, () => _for_item0.value + i); 16 | return n2; 17 | }, void 0, 1); 18 | return n3; 19 | })(); 20 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__object_de_structured_value_with_rest.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, getRestElement as _getRestElement, template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n0 = _createFor(() => list, (_for_item0, _for_key0) => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => _for_item0.value.id + _getRestElement(_for_item0.value, ["id"]) + _for_key0.value); 13 | return n2; 14 | }, ({ id, ...other }, index) => id); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__array_de_structured_value_with_rest.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n0 = _createFor(() => list, (_for_item0, _for_key0) => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => _for_item0.value[0] + _for_item0.value.slice(3) + _for_key0.value + _for_item0.value[1][0] + _for_item0.value[2].bar); 13 | return n2; 14 | }, ([id, [foo], {bar}, ...other], index) => id); 15 | return n0; 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/v_show.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | use common::{error::ErrorCodes, options::TransformOptions}; 4 | use compiler_rs::transform; 5 | use insta::assert_snapshot; 6 | 7 | #[test] 8 | fn basic() { 9 | let code = transform("
", None).code; 10 | assert_snapshot!(code); 11 | } 12 | 13 | #[test] 14 | fn should_raise_error_if_has_no_expression() { 15 | let error = RefCell::new(None); 16 | transform( 17 | "
", 18 | Some(TransformOptions { 19 | on_error: Box::new(|e, _| { 20 | *error.borrow_mut() = Some(e); 21 | }), 22 | ..Default::default() 23 | }), 24 | ); 25 | assert_eq!(*error.borrow(), Some(ErrorCodes::VShowNoExpression)); 26 | } 27 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__expression_object.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, renderEffect as _renderEffect, setProp as _setProp, template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n0 = _createFor(() => Array.from({ length: count.value }).map((_, id) => ({ id })), (_for_item0, _for_key0) => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => _for_item0.value); 13 | _renderEffect(() => _setProp(n2, "id", _for_key0.value)); 14 | return n2; 15 | }); 16 | return n0; 17 | })(); 18 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_if__template.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_if.rs 3 | expression: code 4 | --- 5 | import { child as _child, createIf as _createIf, renderEffect as _renderEffect, setText as _setText, template as _template, toDisplayString as _toDisplayString } from "vue"; 6 | const t0 = _template("
"); 7 | const t1 = _template("hello"); 8 | const t2 = _template("

"); 9 | (() => { 10 | const n0 = _createIf(() => ok, () => { 11 | const n2 = t0(); 12 | const n3 = t1(); 13 | const n4 = t2(); 14 | const x4 = _child(n4); 15 | _renderEffect(() => _setText(x4, _toDisplayString(msg))); 16 | return [ 17 | n2, 18 | n3, 19 | n4 20 | ]; 21 | }); 22 | return n0; 23 | })(); 24 | -------------------------------------------------------------------------------- /packages/eslint/src/rules/index.ts: -------------------------------------------------------------------------------- 1 | import _defineStyle from './define-style' 2 | import jsxSortProps from './jsx-sort-props' 3 | import type { DefineStyleRuleOptions } from './define-style/types' 4 | import type { JsxSortPropsRuleOptions } from './jsx-sort-props/types' 5 | import type { Linter } from 'eslint' 6 | 7 | const ruleOptions = { 8 | 'jsx-sort-props': jsxSortProps, 9 | 'define-style': _defineStyle, 10 | } 11 | 12 | export interface RuleOptions { 13 | 'vue-jsx-vapor/jsx-sort-props': JsxSortPropsRuleOptions 14 | 'vue-jsx-vapor/define-style': DefineStyleRuleOptions 15 | } 16 | 17 | export type Rules = Partial<{ 18 | [K in keyof RuleOptions]: 19 | | Linter.RuleSeverity 20 | | [Linter.RuleSeverity, ...RuleOptions[K]] 21 | }> 22 | 23 | export default ruleOptions 24 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__selector_pattern1.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { child as _child, createFor as _createFor, setText as _setText, template as _template, toDisplayString as _toDisplayString } from "vue"; 6 | const t0 = _template(" "); 7 | (() => { 8 | let _selector0_0; 9 | const n0 = _createFor(() => rows, (_for_item0) => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _selector0_0(() => { 13 | _setText(x2, _toDisplayString(selected === _for_item0.value.id ? "danger" : "")); 14 | }); 15 | return n2; 16 | }, (row) => row.id, void 0, ({ createSelector }) => { 17 | _selector0_0 = createSelector(() => selected); 18 | }); 19 | return n0; 20 | })(); 21 | -------------------------------------------------------------------------------- /packages/eslint/src/rules/jsx-sort-props/types.ts: -------------------------------------------------------------------------------- 1 | export interface JsxSortPropsSchema0 { 2 | callbacksLast?: boolean 3 | shorthandFirst?: boolean 4 | shorthandLast?: boolean 5 | multiline?: 'ignore' | 'first' | 'last' 6 | ignoreCase?: boolean 7 | noSortAlphabetically?: boolean 8 | reservedFirst?: string[] | boolean 9 | reservedLast?: string[] 10 | locale?: string 11 | } 12 | 13 | export type JsxSortPropsRuleOptions = [JsxSortPropsSchema0?] 14 | 15 | export type RuleOptions = JsxSortPropsRuleOptions 16 | export type MessageIds = 17 | | 'listIsEmpty' 18 | | 'listReservedPropsFirst' 19 | | 'listReservedPropsLast' 20 | | 'listCallbacksLast' 21 | | 'listShorthandFirst' 22 | | 'listShorthandLast' 23 | | 'listMultilineFirst' 24 | | 'listMultilineLast' 25 | | 'sortPropsByAlpha' 26 | -------------------------------------------------------------------------------- /packages/compiler-rs/crates/vapor/src/transform/v_once.rs: -------------------------------------------------------------------------------- 1 | use common::directive::find_prop; 2 | use napi::Either; 3 | use oxc_ast::ast::JSXChild; 4 | 5 | use crate::{ 6 | ir::index::BlockIRNode, 7 | transform::{ContextNode, TransformContext}, 8 | }; 9 | 10 | /// # SAFETY 11 | pub unsafe fn transform_v_once<'a>( 12 | context_node: *mut ContextNode<'a>, 13 | context: &'a TransformContext<'a>, 14 | _: &'a mut BlockIRNode<'a>, 15 | _: &'a mut ContextNode<'a>, 16 | ) -> Option> { 17 | let Either::B(node) = (unsafe { &*context_node }) else { 18 | return None; 19 | }; 20 | 21 | if let JSXChild::Element(node) = &node 22 | && find_prop(node, Either::A(String::from("v-once"))).is_some() 23 | { 24 | *context.in_v_once.borrow_mut() = true; 25 | } 26 | None 27 | } 28 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_text__expression_conditional.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_text.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes, createNodes as _createNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createIf as _createIf, template as _template } from "vue"; 7 | const t0 = _template(" "); 8 | const t1 = _template("
fail
"); 9 | (() => { 10 | const n0 = _createIf(() => ok, () => { 11 | const n2 = t0(); 12 | const x2 = _child(n2); 13 | _setNodes(x2, () => msg); 14 | return n2; 15 | }, () => _createIf(() => fail, () => { 16 | const n4 = t1(); 17 | return n4; 18 | }, () => { 19 | const n6 = _createNodes(null); 20 | return n6; 21 | })); 22 | return n0; 23 | })(); 24 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_once__execution_order.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_once.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, next as _next, nthChild as _nthChild, setProp as _setProp, template as _template } from "vue"; 7 | const t0 = _template("

", true); 8 | (() => { 9 | const n4 = t0(); 10 | const n0 = _child(n4); 11 | const n1 = _next(n0); 12 | const n2 = _nthChild(n4, 3); 13 | const n3 = _next(n2); 14 | const x0 = _child(n0); 15 | _setNodes(x0, foo); 16 | _setNodes(n1, () => bar); 17 | _setNodes(n2, () => baz); 18 | _setProp(n3, "foo", true); 19 | const x3 = _child(n3); 20 | _setNodes(x3, () => foo); 21 | return n4; 22 | })(); 23 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_once__with_conditional_expression.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_once.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createIf as _createIf, setInsertionState as _setInsertionState, template as _template } from "vue"; 7 | const t0 = _template(" "); 8 | const t1 = _template("
fail
"); 9 | const t2 = _template("
", true); 10 | (() => { 11 | const n5 = t2(); 12 | _setInsertionState(n5); 13 | const n0 = _createIf(() => ok, () => { 14 | const n2 = t0(); 15 | const x2 = _child(n2); 16 | _setNodes(x2, msg); 17 | return n2; 18 | }, () => { 19 | const n4 = t1(); 20 | return n4; 21 | }, true); 22 | return n5; 23 | })(); 24 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/snapshots/r#mod__transform__hmr__exports_with_define_component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/transform/hmr.rs 3 | expression: code 4 | --- 5 | export const Comp = defineComponent(() => {}); 6 | const __default__ = defineVaporComponent(() => {}); 7 | export default __default__; 8 | Comp.__hmrId = "8ed58763ca2bbfd5"; 9 | __VUE_HMR_RUNTIME__.createRecord("8ed58763ca2bbfd5", Comp); 10 | __default__.__hmrId = "52164bac249078a3"; 11 | __VUE_HMR_RUNTIME__.createRecord("52164bac249078a3", __default__); 12 | if (import.meta.hot) import.meta.hot.accept((mod) => { 13 | __VUE_HMR_RUNTIME__[typeof mod.Comp === "function" ? "rerender" : "reload"](mod.Comp.__hmrId, mod.Comp); 14 | __VUE_HMR_RUNTIME__[typeof mod.default === "function" ? "rerender" : "reload"](mod.default.__hmrId, mod.default); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_bind__with_constant_value.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_bind.rs 3 | expression: code 4 | --- 5 | import { setProp as _setProp, template as _template } from "vue"; 6 | const t0 = _template("
", true); 7 | (() => { 8 | const n0 = t0(); 9 | _setProp(n0, "a", void 0); 10 | _setProp(n0, "b", 1 > 2); 11 | _setProp(n0, "c", 1 + 2); 12 | _setProp(n0, "d", 1 ? 2 : 3); 13 | _setProp(n0, "i", true); 14 | _setProp(n0, "j", null); 15 | _setProp(n0, "l", { foo: 1 }); 16 | _setProp(n0, "n", { ...{ foo: 1 } }); 17 | _setProp(n0, "o", [ 18 | 1, 19 | , 20 | 3 21 | ]); 22 | _setProp(n0, "p", [1, ...[2, 3]]); 23 | _setProp(n0, "q", [1, 2]); 24 | _setProp(n0, "r", /\s+/); 25 | return n0; 26 | })(); 27 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_children__children_sibling_references.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_children.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, next as _next, renderEffect as _renderEffect, setProp as _setProp, template as _template } from "vue"; 7 | const t0 = _template("

", true); 8 | (() => { 9 | const n3 = t0(); 10 | const n0 = _child(n3); 11 | const n1 = _next(n0); 12 | const n2 = _next(n1); 13 | const x0 = _child(n0); 14 | _setNodes(x0, () => first); 15 | _setNodes(n1, "123 ", () => second, " 456 ", () => foo); 16 | const x2 = _child(n2); 17 | _setNodes(x2, () => forth); 18 | _renderEffect(() => _setProp(n3, "id", id)); 19 | return n3; 20 | })(); 21 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_if__v_if_v_if_or_v_elses.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_if.rs 3 | expression: code 4 | --- 5 | import { createIf as _createIf, setInsertionState as _setInsertionState, template as _template } from "vue"; 6 | const t0 = _template("foo"); 7 | const t1 = _template("bar"); 8 | const t2 = _template("baz"); 9 | const t3 = _template("
", true); 10 | (() => { 11 | const n8 = t3(); 12 | _setInsertionState(n8); 13 | const n0 = _createIf(() => "foo", () => { 14 | const n2 = t0(); 15 | return n2; 16 | }); 17 | _setInsertionState(n8); 18 | const n3 = _createIf(() => "bar", () => { 19 | const n5 = t1(); 20 | return n5; 21 | }, () => { 22 | const n7 = t2(); 23 | return n7; 24 | }); 25 | return n8; 26 | })(); 27 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__nested_v_for.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, setInsertionState as _setInsertionState, template as _template } from "vue"; 7 | const t0 = _template(" "); 8 | const t1 = _template("
"); 9 | (() => { 10 | const n0 = _createFor(() => list, (_for_item0) => { 11 | const n5 = t1(); 12 | _setInsertionState(n5); 13 | const n2 = _createFor(() => _for_item0.value, (_for_item1) => { 14 | const n4 = t0(); 15 | const x4 = _child(n4); 16 | _setNodes(x4, () => _for_item1.value + _for_item0.value); 17 | return n4; 18 | }, void 0, 1); 19 | return n5; 20 | }); 21 | return n0; 22 | })(); 23 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/core/vue-jsx.ts: -------------------------------------------------------------------------------- 1 | import { transformSync } from '@babel/core' 2 | // @ts-ignore missing type 3 | import babelTypescript from '@babel/plugin-transform-typescript' 4 | import jsx from '@vue/babel-plugin-jsx' 5 | 6 | export function transformVueJsx( 7 | code: string, 8 | id: string, 9 | needSourceMap = false, 10 | ) { 11 | const result = transformSync(code, { 12 | plugins: [ 13 | jsx, 14 | ...(id.endsWith('.tsx') 15 | ? [[babelTypescript, { isTSX: true, allowExtensions: true }]] 16 | : []), 17 | ], 18 | filename: id, 19 | sourceMaps: needSourceMap, 20 | sourceFileName: id, 21 | babelrc: false, 22 | configFile: false, 23 | }) 24 | if (result?.code && result.code !== code) { 25 | return { 26 | code: result.code, 27 | map: result.map, 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/snapshots/r#mod__transform__interop__basic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/transform/interop.rs 3 | expression: code 4 | --- 5 | import { createNodes as _createNodes } from "vue-jsx-vapor"; 6 | import { template as _template } from "vue"; 7 | const t0 = _template("", true); 8 | const t1 = _template("
"); 9 | const t2 = _template("
", true); 10 | const A = defineComponent(() => { 11 | defineVaporComponent(() => (() => { 12 | const n0 = t0(); 13 | return n0; 14 | })()); 15 | return () =>
; 16 | }); 17 | const B = defineVaporComponent(() => { 18 | const C = defineComponent(() =>
); 19 | const D = (() => { 20 | const n2 = t1(); 21 | const n0 = _createNodes(() => foo, " "); 22 | return [n0, n2]; 23 | })(); 24 | return (() => { 25 | const n0 = t2(); 26 | return n0; 27 | })(); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_children__efficient_traversal.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_children.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, next as _next, template as _template } from "vue"; 7 | const t0 = _template("
x
", true); 8 | (() => { 9 | const n3 = t0(); 10 | const p0 = _next(_child(n3)); 11 | const p1 = _next(p0); 12 | const p2 = _next(p1); 13 | const n2 = _child(p2); 14 | const n1 = _child(p1); 15 | const n0 = _child(p0); 16 | const x0 = _child(n0); 17 | _setNodes(x0, () => ({ msg })); 18 | const x1 = _child(n1); 19 | _setNodes(x1, () => ({ msg })); 20 | const x2 = _child(n2); 21 | _setNodes(x2, () => ({ msg })); 22 | return n3; 23 | })(); 24 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_template_ref__function_ref.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_template_ref.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { createTemplateRefSetter as _createTemplateRefSetter, renderEffect as _renderEffect, template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const _setTemplateRef = _createTemplateRefSetter(); 10 | const n3 = _createComponent(Comp, null, { default: (_slotProps0) => { 11 | const n1 = t0(); 12 | let r1; 13 | _renderEffect(() => r1 = _setTemplateRef(n1, (bar) => { 14 | foo.value = bar; 15 | ({baz: _slotProps0.baz, bar: _slotProps0.baz} = bar); 16 | console.log(foo.value, _slotProps0.baz); 17 | }, r1)); 18 | return n1; 19 | } }, true); 20 | return n3; 21 | })(); 22 | -------------------------------------------------------------------------------- /packages/compiler-rs/crates/vapor/src/generate/html.rs: -------------------------------------------------------------------------------- 1 | use oxc_ast::NONE; 2 | use oxc_ast::ast::Statement; 3 | use oxc_span::SPAN; 4 | 5 | use crate::generate::CodegenContext; 6 | use crate::generate::expression::gen_expression; 7 | use crate::ir::index::SetHtmlIRNode; 8 | 9 | pub fn gen_set_html<'a>(oper: SetHtmlIRNode<'a>, context: &'a CodegenContext<'a>) -> Statement<'a> { 10 | let ast = &context.ast; 11 | let SetHtmlIRNode { value, element, .. } = oper; 12 | 13 | ast.statement_expression( 14 | SPAN, 15 | ast.expression_call( 16 | SPAN, 17 | ast.expression_identifier(SPAN, ast.atom(&context.helper("setHtml"))), 18 | NONE, 19 | ast.vec_from_array([ 20 | ast 21 | .expression_identifier(SPAN, ast.atom(&format!("n{element}"))) 22 | .into(), 23 | gen_expression(value, context, None, None).into(), 24 | ]), 25 | false, 26 | ), 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /packages/macros/tests/fixtures/define-style.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, ref } from 'vue' 2 | 3 | export const Comp = () => { 4 | const color = ref('red') 5 | defineStyle(` 6 | .foo { 7 | color: ${color.value}; 8 | } 9 | `) 10 | return
foo
11 | } 12 | 13 | export default defineComponent(() => { 14 | const color = ref('red') 15 | const styles = defineStyle.scss(` 16 | .bar { 17 | color: ${color.value}; 18 | .bar-baz { 19 | background: red; 20 | } 21 | } 22 | `) 23 | const { default: Default, ...slots } = defineSlots() 24 | return () => ( 25 | <> 26 |
foo
27 |
28 | bar 29 |
30 | 31 | 32 | 33 | 34 | ) 35 | }) 36 | 37 | defineStyle.scss(` 38 | .bar { 39 | color: red; 40 | } 41 | `) 42 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__custom_directive__component.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/custom_directive.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { createIf as _createIf, setInsertionState as _setInsertionState, template as _template, withVaporDirectives as _withVaporDirectives } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n0 = _createComponent(Comp, null, { default: () => { 10 | const n2 = _createIf(() => true, () => { 11 | const n5 = t0(); 12 | _setInsertionState(n5); 13 | const n4 = _createComponent(Bar); 14 | _withVaporDirectives(n4, [[ 15 | vHello, 16 | void 0, 17 | void 0, 18 | { world: true } 19 | ]]); 20 | return n5; 21 | }, null, true); 22 | return n2; 23 | } }, true); 24 | _withVaporDirectives(n0, [[vTest]]); 25 | return n0; 26 | })(); 27 | -------------------------------------------------------------------------------- /eslint.config.ts: -------------------------------------------------------------------------------- 1 | import { sxzz } from '@sxzz/eslint-config' 2 | import vueJsxVapor from './packages/eslint/src/index' 3 | 4 | export default [ 5 | { 6 | ignores: ['**/wasi-worker**', '**/compiler-rs.wasi**'], 7 | }, 8 | ...(await sxzz() 9 | .removeRules( 10 | 'unicorn/filename-case', 11 | 'import/no-default-export', 12 | 'unicorn/no-new-array', 13 | 'unicorn/prefer-dom-node-remove', 14 | 'unused-imports/no-unused-imports', 15 | '@eslint-community/eslint-comments/no-unlimited-disable', 16 | ) 17 | .append([ 18 | { 19 | name: 'docs', 20 | files: ['**/*.md/*.tsx'], 21 | rules: { 22 | 'no-var': 'off', 23 | 'no-mutable-exports': 'off', 24 | 'no-duplicate-imports': 'off', 25 | 'import/first': 'off', 26 | 'unused-imports/no-unused-vars': 'off', 27 | }, 28 | }, 29 | ])), 30 | vueJsxVapor({ 31 | ignores: ['**/docs/**'], 32 | }), 33 | ] 34 | -------------------------------------------------------------------------------- /packages/runtime/src/block.ts: -------------------------------------------------------------------------------- 1 | import { createTextNode, isFragment, isVaporComponent, type Block } from 'vue' 2 | 3 | type NodeChildAtom = Block | string | number | boolean | null | undefined | void 4 | 5 | export type NodeArrayChildren = Array 6 | 7 | export type NodeChild = NodeChildAtom | NodeArrayChildren 8 | 9 | export function normalizeNode(node: NodeChild): Block { 10 | if (node == null || typeof node === 'boolean') { 11 | return document.createComment('') 12 | } else if (Array.isArray(node) && node.length) { 13 | return node.map(normalizeNode) 14 | } else if (isBlock(node)) { 15 | return node 16 | } else { 17 | // strings and numbers 18 | return createTextNode(String(node)) 19 | } 20 | } 21 | 22 | export function isBlock(val: NonNullable): val is Block { 23 | return ( 24 | val instanceof Node || 25 | Array.isArray(val) || 26 | isVaporComponent(val) || 27 | isFragment(val) 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_for__identifiers.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_for.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes } from "vue-jsx-vapor"; 6 | import { child as _child, createFor as _createFor, renderEffect as _renderEffect, setProp as _setProp, template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | (() => { 9 | const n0 = _createFor(() => items, (_for_item0, _for_key0) => { 10 | const n2 = t0(); 11 | const x2 = _child(n2); 12 | _setNodes(x2, () => ((item) => { 13 | let index = 1; 14 | return [item, index]; 15 | })(_for_item0.value), () => (() => { 16 | switch (_for_item0.value) { 17 | case _for_key0.value: { 18 | let item = ""; 19 | return `${[item, _for_key0.value]}`; 20 | } 21 | } 22 | })()); 23 | _renderEffect(() => _setProp(n2, "id", _for_key0.value)); 24 | return n2; 25 | }); 26 | return n0; 27 | })(); 28 | -------------------------------------------------------------------------------- /docs/zh/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "Vue JSX Vapor" 7 | text: "类型安全、高性能、更舒适的开发体验" 8 | tagline: Vue JSX 的 Vapor 模式 9 | image: 10 | src: /logo.svg 11 | alt: Vue JSX Vapor 12 | actions: 13 | - theme: brand 14 | text: 快速上手 15 | link: /zh/introduction/getting-started 16 | - theme: alt 17 | text: 功能特性 18 | link: /zh/features/directives 19 | 20 | features: 21 | - icon: ⚒️ ️ 22 | title: 指令 23 | details: 支持 Vue 的所有内置指令。 24 | - icon: ✨ 25 | title: 宏 26 | details: 支持 Vue 的大部分宏,对 JSX 友好。 27 | - icon: 🦾 28 | title: 类型安全 29 | details: 通过安装 TS Macro (VSCode 插件) 提供 Volar 插件支持。 30 | - icon: ⚡️ 31 | title: 高性能 32 | details: 拥有与 Vue Vapor 同等的性能! 33 | - icon: 🦀 34 | title: Rust 编译器 35 | details: 基于 Oxc,相比于 Babel 插件 性能提升了20倍。 36 | - icon: ⚙️ 37 | title: ESLint 38 | details: 提供 ESLint 插件为 vue-jsx-vapor 自动格式化代码。 39 | --- 40 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_text__expression_map.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_text.rs 3 | expression: code 4 | --- 5 | import { setNodes as _setNodes, createNodes as _createNodes } from "vue-jsx-vapor"; 6 | import { child as _child, template as _template } from "vue"; 7 | const t0 = _template("
1
", true); 8 | const t1 = _template(" ", true); 9 | const t2 = _template("
", true); 10 | (() => { 11 | const n0 = _createNodes(() => Array.from({ length: count.value }).map((_, index) => { 12 | if (index > 1) { 13 | return (() => { 14 | const n0 = t0(); 15 | return n0; 16 | })(); 17 | } else { 18 | return [(() => { 19 | const n0 = t1(); 20 | const x0 = _child(n0); 21 | _setNodes(x0, "(", () => index, ") lt 1"); 22 | return n0; 23 | })(), (() => { 24 | const n0 = t2(); 25 | return n0; 26 | })()]; 27 | } 28 | })); 29 | return n0; 30 | })(); 31 | -------------------------------------------------------------------------------- /packages/compiler-rs/wasi-worker-browser.mjs: -------------------------------------------------------------------------------- 1 | import { instantiateNapiModuleSync, MessageHandler, WASI } from '@napi-rs/wasm-runtime' 2 | 3 | const handler = new MessageHandler({ 4 | onLoad({ wasmModule, wasmMemory }) { 5 | const wasi = new WASI({ 6 | print: function () { 7 | // eslint-disable-next-line no-console 8 | console.log.apply(console, arguments) 9 | }, 10 | printErr: function() { 11 | // eslint-disable-next-line no-console 12 | console.error.apply(console, arguments) 13 | }, 14 | }) 15 | return instantiateNapiModuleSync(wasmModule, { 16 | childThread: true, 17 | wasi, 18 | overwriteImports(importObject) { 19 | importObject.env = { 20 | ...importObject.env, 21 | ...importObject.napi, 22 | ...importObject.emnapi, 23 | memory: wasmMemory, 24 | } 25 | }, 26 | }) 27 | }, 28 | }) 29 | 30 | globalThis.onmessage = function (e) { 31 | handler.handle(e) 32 | } 33 | -------------------------------------------------------------------------------- /packages/eslint/test/define-style.spec.ts: -------------------------------------------------------------------------------- 1 | import { createRuleTester } from 'eslint-vitest-rule-tester' 2 | import { describe, expect, it } from 'vitest' 3 | import _defineStyle from '../src/rules/define-style' 4 | 5 | describe('define-style', () => { 6 | const { invalid } = createRuleTester({ 7 | name: 'define-style', 8 | rule: _defineStyle, 9 | }) 10 | 11 | it('basic', async () => { 12 | const { result } = await invalid({ 13 | code: ` 14 | defineStyle(\` .foo { color: red; } \`) 15 | `, 16 | errors: ['define-style'], 17 | }) 18 | expect(result.output).toMatchSnapshot() 19 | }) 20 | 21 | it('syntax error', async () => { 22 | const { result } = await invalid({ 23 | code: ` 24 | defineStyle(\` 25 | .foo { 26 | color: red 27 | background: blue; 28 | } 29 | \`) 30 | `, 31 | errors: ['define-style-syntax-error'], 32 | }) 33 | expect(result.output).toMatchSnapshot() 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/volar.ts: -------------------------------------------------------------------------------- 1 | import jsxMacros from '@vue-jsx-vapor/macros/volar' 2 | import jsxDirective from '@vue-macros/volar/jsx-directive' 3 | import jsxRef from '@vue-macros/volar/jsx-ref' 4 | import { createPlugin, type PluginReturn } from 'ts-macro' 5 | import jsxElement from './volar/jsx-element' 6 | import type { Options } from './options' 7 | 8 | const plugin: PluginReturn = createPlugin( 9 | (ctx, options = ctx.vueCompilerOptions?.['vue-jsx-vapor']) => { 10 | return [ 11 | jsxDirective()(ctx), 12 | options?.ref === false 13 | ? [] 14 | : jsxRef(options?.ref === true ? undefined : options?.ref)(ctx), 15 | options?.macros === false 16 | ? [] 17 | : options?.macros 18 | ? jsxMacros(options.macros === true ? undefined : options.macros)(ctx) 19 | : [], 20 | options?.interop ? [] : jsxElement()(ctx), 21 | ].flat() 22 | }, 23 | ) 24 | 25 | export default plugin 26 | export { plugin as 'module.exports' } 27 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/transform_template_ref.rs: -------------------------------------------------------------------------------- 1 | use compiler_rs::transform; 2 | use insta::assert_snapshot; 3 | 4 | #[test] 5 | fn static_ref() { 6 | let code = transform("
", None).code; 7 | assert_snapshot!(code); 8 | } 9 | 10 | #[test] 11 | fn dynamic_ref() { 12 | let code = transform("
", None).code; 13 | assert_snapshot!(code); 14 | } 15 | 16 | #[test] 17 | fn function_ref() { 18 | let code = transform( 19 | " 20 |
{ 21 | foo.value = bar 22 | ;({ baz, bar: baz } = bar) 23 | console.log(foo.value, baz) 24 | }} /> 25 | ", 26 | None, 27 | ) 28 | .code; 29 | assert_snapshot!(code); 30 | } 31 | 32 | #[test] 33 | fn ref_v_if() { 34 | let code = transform("
", None).code; 35 | assert_snapshot!(code); 36 | } 37 | 38 | #[test] 39 | fn ref_v_for() { 40 | let code = transform("
", None).code; 41 | assert_snapshot!(code); 42 | } 43 | -------------------------------------------------------------------------------- /packages/eslint/src/index.ts: -------------------------------------------------------------------------------- 1 | import rules, { type Rules } from './rules' 2 | import type { Linter } from 'eslint' 3 | 4 | export const plugins = { 5 | 'vue-jsx-vapor': { 6 | rules, 7 | }, 8 | } 9 | 10 | export { rules, type Rules } 11 | 12 | export default ({ rules = {}, ...options }: Linter.Config = {}) => ({ 13 | name: 'vue-jsx-vapor', 14 | plugins, 15 | rules: { 16 | 'style/jsx-sort-props': 'off', 17 | 'react/jsx-sort-props': 'off', 18 | 'vue-jsx-vapor/jsx-sort-props': rules['vue-jsx-vapor/jsx-sort-props'] || [ 19 | 'warn', 20 | { 21 | callbacksLast: true, 22 | shorthandFirst: true, 23 | reservedFirst: [ 24 | 'v-if', 25 | 'v-else-if', 26 | 'v-else', 27 | 'v-for', 28 | 'key', 29 | 'ref', 30 | 'v-model', 31 | ], 32 | reservedLast: ['v-slot', 'v-slots', 'v-text', 'v-html'], 33 | }, 34 | ], 35 | 'vue-jsx-vapor/define-style': rules['vue-jsx-vapor/define-style'] || 'warn', 36 | } satisfies Rules & Record, 37 | ...options, 38 | }) 39 | -------------------------------------------------------------------------------- /playground/src/element.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable unused-imports/no-unused-vars */ 2 | /* eslint-disable @typescript-eslint/no-unused-expressions */ 3 | /* eslint-disable @typescript-eslint/consistent-type-assertions */ 4 | import { computed, defineComponent, defineVaporComponent } from 'vue' 5 | 6 | const Comp = () => { 7 | const A = 8 | A.href = '#foo' 9 | return A 10 | } 11 | const comp = 12 | comp.block.href 13 | 14 | const VaporComp = defineVaporComponent((props: { id: number }) => { 15 | defineSlots({ 16 | default: (props: { id: 1 }) => [], 17 | }) 18 | defineExpose({ 19 | id: computed(() => 1), 20 | }) 21 | return
{props.id}
22 | }) 23 | const vaporComp = 24 | vaporComp.props.id 25 | vaporComp.exposeProxy?.id === ({} as number) 26 | vaporComp.block.style 27 | 28 | const VDomComp = defineComponent((props: { id: number }) => { 29 | defineSlots({ 30 | default: (props: { id: 1 }) =>
, 31 | }) 32 | return () =>
{props.id}
33 | }) 34 | const vdompComp = 35 | vdompComp.props 36 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__transform_children__next_child_and_nthchild_should_be_above_the_set_insertion_state.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/transform_children.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { child as _child, createIf as _createIf, next as _next, nthChild as _nthChild, renderEffect as _renderEffect, setInsertionState as _setInsertionState, setProp as _setProp, template as _template } from "vue"; 7 | const t0 = _template("
"); 8 | const t1 = _template("
", true); 9 | (() => { 10 | const n6 = t1(); 11 | const n5 = _next(_child(n6)); 12 | const n7 = _nthChild(n6, 3); 13 | const p0 = _next(n7); 14 | const n4 = _child(p0); 15 | _setInsertionState(n6, n5); 16 | const n0 = _createComponent(Comp); 17 | _setInsertionState(n6, n7); 18 | const n1 = _createIf(() => true, () => { 19 | const n3 = t0(); 20 | return n3; 21 | }, null, true); 22 | _renderEffect(() => _setProp(n4, "disabled", foo)); 23 | return n6; 24 | })(); 25 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/snapshots/r#mod__vapor__v_slot__dynamic_slots_name_with_v_if_and_v_else_if.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/vapor/v_slot.rs 3 | expression: code 4 | --- 5 | import { createComponent as _createComponent } from "vue-jsx-vapor"; 6 | import { template as _template } from "vue"; 7 | const t0 = _template("condition slot"); 8 | const t1 = _template("another condition"); 9 | const t2 = _template("other condition"); 10 | const t3 = _template("else condition"); 11 | (() => { 12 | const n13 = _createComponent(Comp, null, { $: [() => condition ? { 13 | name: "condition", 14 | fn: () => { 15 | const n1 = t0(); 16 | return n1; 17 | } 18 | } : anotherCondition ? { 19 | name: "condition", 20 | fn: (_slotProps0) => { 21 | const n4 = t1(); 22 | return n4; 23 | } 24 | } : otherCondition ? { 25 | name: "condition", 26 | fn: () => { 27 | const n7 = t2(); 28 | return n7; 29 | } 30 | } : { 31 | name: "condition", 32 | fn: () => { 33 | const n10 = t3(); 34 | return n10; 35 | } 36 | }] }, true); 37 | return n13; 38 | })(); 39 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* 3 | - playground 4 | - docs 5 | - benchmark 6 | 7 | defines: 8 | - &babel ^7.28.4 9 | - &vue-macros ^3.0.1 10 | - &ts-macro ^0.3.6 11 | - &vue 3.6.0-alpha.2 12 | 13 | catalog: 14 | '@babel/core': *babel 15 | '@babel/parser': *babel 16 | '@babel/plugin-syntax-jsx': ^7.27.1 17 | '@babel/plugin-transform-typescript': ^7.28.0 18 | '@babel/traverse': *babel 19 | '@babel/types': *babel 20 | '@types/babel__core': ^7.20.5 21 | 22 | '@nuxt/kit': ^3.19.2 23 | '@nuxt/schema': ^3.19.2 24 | 25 | '@vue/compiler-sfc': *vue 26 | '@vue/shared': *vue 27 | 28 | '@vue-macros/common': *vue-macros 29 | '@vue-macros/define-render': *vue-macros 30 | '@vue-macros/jsx-directive': *vue-macros 31 | '@vue-macros/test-utils': *vue-macros 32 | '@vue-macros/volar': *vue-macros 33 | 34 | '@ts-macro/tsc': *ts-macro 35 | '@types/hash-sum': ^1.0.2 36 | ast-kit: ^2.1.2 37 | hash-sum: ^2.0.0 38 | source-map-js: ^1.2.1 39 | ts-macro: *ts-macro 40 | unplugin: ^2.3.10 41 | unplugin-utils: ^0.2.5 42 | vite: ^7.1.6 43 | vitest: ^3.2.4 44 | vue: *vue 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 zhiyuanzmj 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/runtime/src/helpers.ts: -------------------------------------------------------------------------------- 1 | import { proxyRefs, toRefs, useAttrs, type GenericComponentInstance } from 'vue' 2 | import * as Vue from 'vue' 3 | 4 | export function getCurrentInstance(): GenericComponentInstance | null { 5 | // @ts-ignore 6 | return Vue.currentInstance || Vue.getCurrentInstance() 7 | } 8 | 9 | /** 10 | * Returns the props of the current component instance. 11 | * 12 | * @example 13 | * ```tsx 14 | * import { useProps } from 'vue-jsx-vapor' 15 | * 16 | * defineComponent(({ foo = '' })=>{ 17 | * const props = useProps() // { foo: '' } 18 | * }) 19 | * ``` 20 | */ 21 | export function useProps() { 22 | const i = getCurrentInstance() 23 | return i!.props 24 | } 25 | 26 | /** 27 | * Returns the merged props and attrs of the current component.\ 28 | * Equivalent to `useProps()` + `useAttrs()`. 29 | * 30 | * @example 31 | * ```tsx 32 | * import { useFullProps } from 'vue-jsx-vapor' 33 | * 34 | * defineComponent((props) => { 35 | * const fullProps = useFullProps() // = useAttrs() + useProps() 36 | * }) 37 | */ 38 | export function useFullProps() { 39 | return proxyRefs({ 40 | ...toRefs(useProps()), 41 | ...toRefs(useAttrs()), 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /playground/src/slot.tsx: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | const Comp = (props: { foo: string }, { slots }) => { 4 | return ( 5 | <> 6 | {slots.default ? ( 7 | 8 | ) : ( 9 |
default slot
10 | )} 11 | 12 | ) 13 | } 14 | 15 | const slots = { 16 | default: (scope) =>
{scope.foo}
, 17 | } 18 | 19 | // eslint-disable-next-line unused-imports/no-unused-vars 20 | const slotName = ref('default') 21 | export default () => { 22 | const foo = ref('foo') 23 | return ( 24 | <> 25 | 26 |
27 |
28 | v-slots 29 | 30 | 31 |
{scope.foo}
}} 34 | /> 35 |
36 | 37 |
38 | v-slot 39 | 40 |
{scope.foo}
41 |
42 |
43 |
44 | 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /packages/compiler-rs/benches/bench.rs: -------------------------------------------------------------------------------- 1 | use compiler_rs::transform; 2 | use criterion::{Criterion, criterion_group, criterion_main}; 3 | 4 | fn bench_compile(b: &mut Criterion) { 5 | b.bench_function("compile", |b| { 6 | b.iter(|| { 7 | transform( 8 | &format!( 9 | "<>{}", 10 | " alert(1)} 14 | v-show={true} 15 | v-model={foo} 16 | v-once 17 | v-slot={foo} 18 | > 19 |
24 | {item} 25 |
26 | 27 | bar 28 | 29 | 30 | default 31 | 34 | 35 |
" 36 | .repeat(12) 37 | ), 38 | None, 39 | ) 40 | }) 41 | }); 42 | } 43 | 44 | criterion_group!(benches, bench_compile); 45 | criterion_main!(benches); 46 | -------------------------------------------------------------------------------- /packages/macros/src/volar.ts: -------------------------------------------------------------------------------- 1 | import { createFilter, REGEX_VUE_SFC } from '@vue-macros/common' 2 | import { createPlugin, type PluginReturn } from 'ts-macro' 3 | import { resolveOptions, type Options } from './options' 4 | import { getGlobalTypes, getRootMap, transformJsxMacros } from './volar/index' 5 | 6 | const plugin: PluginReturn = createPlugin( 7 | ({ ts }, userOptions = {}) => { 8 | const resolvedOptions = resolveOptions(userOptions!) 9 | ;(resolvedOptions.include as any[]).push(REGEX_VUE_SFC) 10 | const filter = createFilter(resolvedOptions) 11 | 12 | return { 13 | name: '@vue-jsx-vapor/macros', 14 | resolveVirtualCode(virtualCode) { 15 | const { filePath, codes } = virtualCode 16 | if (!filter(filePath)) return 17 | 18 | const options = { 19 | ts, 20 | ...virtualCode, 21 | ...resolvedOptions, 22 | } 23 | const rootMap = getRootMap(options) 24 | if (rootMap.size) { 25 | transformJsxMacros(rootMap, options) 26 | } 27 | codes.push(getGlobalTypes(rootMap, options)) 28 | }, 29 | } 30 | }, 31 | ) 32 | export default plugin 33 | export { plugin as 'module.exports' } 34 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/v_text.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | use common::{error::ErrorCodes, options::TransformOptions}; 4 | use compiler_rs::transform; 5 | use insta::assert_snapshot; 6 | 7 | #[test] 8 | fn should_convert_v_text_to_set_text() { 9 | let code = transform("
", None).code; 10 | assert_snapshot!(code); 11 | } 12 | 13 | #[test] 14 | fn should_raise_error_and_ignore_children_when_v_text_is_present() { 15 | let error = RefCell::new(None); 16 | transform( 17 | "
hello
", 18 | Some(TransformOptions { 19 | on_error: Box::new(|e, _| { 20 | *error.borrow_mut() = Some(e); 21 | }), 22 | ..Default::default() 23 | }), 24 | ); 25 | assert_eq!(*error.borrow(), Some(ErrorCodes::VTextWithChildren)); 26 | } 27 | 28 | #[test] 29 | fn should_raise_error_if_has_no_expression() { 30 | let error = RefCell::new(None); 31 | transform( 32 | "
", 33 | Some(TransformOptions { 34 | on_error: Box::new(|e, _| { 35 | *error.borrow_mut() = Some(e); 36 | }), 37 | ..Default::default() 38 | }), 39 | ); 40 | assert_eq!(*error.borrow(), Some(ErrorCodes::VTextNoExpression)); 41 | } 42 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "Vue JSX Vapor" 7 | text: "Type-safe, Improve DX, High Performance" 8 | tagline: Vapor Mode of Vue JSX 9 | image: 10 | src: /logo.svg 11 | alt: Vue JSX Vapor 12 | actions: 13 | - theme: brand 14 | text: Get Started 15 | link: /introduction/getting-started 16 | - theme: alt 17 | text: Features 18 | link: /features/directives 19 | 20 | features: 21 | - icon: ⚒️ ️ 22 | title: Directives 23 | details: Support all build-in directives of Vue. 24 | - icon: ✨ 25 | title: Macros 26 | details: Support most macros of Vue, Friendly to JSX. 27 | - icon: 🦾 28 | title: Type Safe 29 | details: Provide Volar plugin support by install TS Macro (VSCode plugin). 30 | - icon: ⚡️ 31 | title: High Performance 32 | details: It has the same performance as Vue Vapor! 33 | - icon: 🦀 34 | title: Compiler rewritten in Rust 35 | details: Based on Oxc, 20x performance improvement over Babel plugin. 36 | - icon: ⚙️ 37 | title: ESLint 38 | details: Provide an ESLint plugin for vue-jsx-vapor to automatically format directives and macros. 39 | --- 40 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/v_html.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | use common::{error::ErrorCodes, options::TransformOptions}; 4 | use compiler_rs::transform; 5 | use insta::assert_snapshot; 6 | 7 | #[test] 8 | fn should_convert_v_html_to_inner_html() { 9 | let code = transform("
", None).code; 10 | assert_snapshot!(code); 11 | } 12 | 13 | #[test] 14 | fn should_raise_error_and_ignore_children_when_v_html_is_present() { 15 | let error = RefCell::new(None); 16 | transform( 17 | "
hello
", 18 | Some(TransformOptions { 19 | on_error: Box::new(|e, _| { 20 | *error.borrow_mut() = Some(e); 21 | }), 22 | ..Default::default() 23 | }), 24 | ); 25 | assert_eq!(*error.borrow(), Some(ErrorCodes::VHtmlWithChildren)); 26 | } 27 | 28 | #[test] 29 | fn should_raise_error_if_has_no_expression() { 30 | let error = RefCell::new(None); 31 | transform( 32 | "
", 33 | Some(TransformOptions { 34 | on_error: Box::new(|e, _| { 35 | *error.borrow_mut() = Some(e); 36 | }), 37 | ..Default::default() 38 | }), 39 | ); 40 | assert_eq!(*error.borrow(), Some(ErrorCodes::VHtmlNoExpression)); 41 | } 42 | -------------------------------------------------------------------------------- /packages/runtime/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vue-jsx-vapor/runtime", 3 | "version": "3.0.4", 4 | "description": "Vue JSX Vapor Runtime", 5 | "type": "module", 6 | "keywords": [ 7 | "vue", 8 | "jsx", 9 | "vapor", 10 | "runtime" 11 | ], 12 | "license": "MIT", 13 | "homepage": "https://github.com/vuejs/vue-jsx-vapor#readme", 14 | "bugs": { 15 | "url": "https://github.com/vuejs/vue-jsx-vapor/issues" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/vuejs/vue-jsx-vapor.git" 20 | }, 21 | "files": [ 22 | "dist" 23 | ], 24 | "main": "dist/index.cjs", 25 | "module": "dist/index.js", 26 | "types": "dist/index.d.ts", 27 | "exports": { 28 | ".": { 29 | "types": "./dist/index.d.ts", 30 | "jsx-vapor-dev": "./src/index.ts", 31 | "require": "./dist/index.cjs", 32 | "import": "./dist/index.js" 33 | }, 34 | "./*": "./*" 35 | }, 36 | "scripts": { 37 | "build": "tsdown", 38 | "dev": "DEV=true tsdown", 39 | "release": "bumpp && npm publish", 40 | "test": "vitest" 41 | }, 42 | "peerDependencies": { 43 | "vue": "^3.6.0-alpha.2" 44 | }, 45 | "devDependencies": { 46 | "csstype": "^3.1.3" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/compiler-rs/crates/vapor/src/transform/v_show.rs: -------------------------------------------------------------------------------- 1 | use napi::bindgen_prelude::Either16; 2 | use oxc_ast::ast::{JSXAttribute, JSXElement}; 3 | 4 | use crate::{ 5 | ir::index::{BlockIRNode, DirectiveIRNode}, 6 | transform::{DirectiveTransformResult, TransformContext}, 7 | }; 8 | use common::{directive::resolve_directive, error::ErrorCodes, expression::SimpleExpressionNode}; 9 | 10 | pub fn transform_v_show<'a>( 11 | _dir: &'a mut JSXAttribute<'a>, 12 | _: &JSXElement, 13 | context: &'a TransformContext<'a>, 14 | context_block: &'a mut BlockIRNode<'a>, 15 | ) -> Option> { 16 | let mut dir = resolve_directive(_dir, context.ir.borrow().source); 17 | if dir.exp.is_none() { 18 | context.options.on_error.as_ref()(ErrorCodes::VShowNoExpression, dir.loc); 19 | dir.exp = Some(SimpleExpressionNode::default()) 20 | } 21 | 22 | let element = context.reference(&mut context_block.dynamic); 23 | context.register_operation( 24 | context_block, 25 | Either16::M(DirectiveIRNode { 26 | directive: true, 27 | element, 28 | dir, 29 | name: String::from("show"), 30 | builtin: Some(true), 31 | asset: None, 32 | model_type: None, 33 | }), 34 | None, 35 | ); 36 | None 37 | } 38 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/transform/snapshots/r#mod__transform__hmr__exports.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/transform/hmr.rs 3 | expression: code 4 | --- 5 | const Comp = () => {}; 6 | function Comp1() {} 7 | export { Comp, Comp1 }; 8 | export function Comp2() {} 9 | const __default__ = function() {}; 10 | export default __default__; 11 | Comp.__hmrId = "8ed58763ca2bbfd5"; 12 | __VUE_HMR_RUNTIME__.createRecord("8ed58763ca2bbfd5", Comp); 13 | Comp1.__hmrId = "f144a08cc37ed966"; 14 | __VUE_HMR_RUNTIME__.createRecord("f144a08cc37ed966", Comp1); 15 | Comp2.__hmrId = "c36ea49ad2d3847e"; 16 | __VUE_HMR_RUNTIME__.createRecord("c36ea49ad2d3847e", Comp2); 17 | __default__.__hmrId = "52164bac249078a3"; 18 | __VUE_HMR_RUNTIME__.createRecord("52164bac249078a3", __default__); 19 | if (import.meta.hot) import.meta.hot.accept((mod) => { 20 | __VUE_HMR_RUNTIME__[typeof mod.Comp === "function" ? "rerender" : "reload"](mod.Comp.__hmrId, mod.Comp); 21 | __VUE_HMR_RUNTIME__[typeof mod.Comp1 === "function" ? "rerender" : "reload"](mod.Comp1.__hmrId, mod.Comp1); 22 | __VUE_HMR_RUNTIME__[typeof mod.Comp2 === "function" ? "rerender" : "reload"](mod.Comp2.__hmrId, mod.Comp2); 23 | __VUE_HMR_RUNTIME__[typeof mod.default === "function" ? "rerender" : "reload"](mod.default.__hmrId, mod.default); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/src/core/ssr.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentOptions } from 'vue' 2 | 3 | export const ssrRegisterHelperId = '/__vue-jsx-ssr-register-helper' 4 | export const ssrRegisterHelperCode = 5 | `import { useSSRContext } from "vue"\n` + 6 | // the const here is just to work around the Bun bug where 7 | // Function.toString() isn't working as intended 8 | // https://github.com/oven-sh/bun/issues/9543 9 | `export const ssrRegisterHelper = ${ssrRegisterHelper.toString()}` 10 | 11 | /** 12 | * This function is serialized with toString() and evaluated as a virtual 13 | * module during SSR 14 | */ 15 | function ssrRegisterHelper(comp: ComponentOptions, filename: string) { 16 | if (typeof comp === 'function') { 17 | // @ts-ignore 18 | comp.__setup = () => { 19 | // @ts-ignore 20 | const ssrContext = useSSRContext() 21 | ;(ssrContext.modules || (ssrContext.modules = new Set())).add(filename) 22 | } 23 | } else { 24 | const setup = comp.setup 25 | comp.setup = (props, ctx) => { 26 | // @ts-ignore 27 | const ssrContext = useSSRContext() 28 | ;(ssrContext.modules || (ssrContext.modules = new Set())).add(filename) 29 | if (setup) { 30 | return setup(props, ctx) 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/eslint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vue-jsx-vapor/eslint", 3 | "version": "3.0.4", 4 | "description": "Vue JSX Vapor ESLint Plugin", 5 | "type": "module", 6 | "keywords": [ 7 | "vue", 8 | "jsx", 9 | "vapor", 10 | "eslint" 11 | ], 12 | "license": "MIT", 13 | "homepage": "https://github.com/vuejs/vue-jsx-vapor#readme", 14 | "bugs": { 15 | "url": "https://github.com/vuejs/vue-jsx-vapor/issues" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/vuejs/vue-jsx-vapor.git" 20 | }, 21 | "files": [ 22 | "dist" 23 | ], 24 | "main": "dist/index.cjs", 25 | "module": "dist/index.js", 26 | "types": "dist/index.d.ts", 27 | "exports": { 28 | ".": { 29 | "types": "./dist/index.d.ts", 30 | "jsx-vapor-dev": "./src/index.ts", 31 | "require": "./dist/index.cjs", 32 | "import": "./dist/index.js" 33 | }, 34 | "./*": "./*" 35 | }, 36 | "scripts": { 37 | "build": "tsdown", 38 | "dev": "DEV=true tsdown", 39 | "release": "bumpp && npm publish", 40 | "test": "vitest" 41 | }, 42 | "dependencies": { 43 | "@prettier/sync": "^0.6.1" 44 | }, 45 | "devDependencies": { 46 | "@typescript-eslint/utils": "^8.44.0", 47 | "eslint-vitest-rule-tester": "^2.2.1" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/macros/src/core/helper/with-defaults.ts: -------------------------------------------------------------------------------- 1 | function resolveDefaultProps(paths: Record): any { 2 | const result: Record = {} 3 | 4 | for (const path of Object.keys(paths)) { 5 | const segments = path.split(/[.?[\]]/).filter(Boolean) 6 | let current = result 7 | 8 | for (let i = 0; i < segments.length; i++) { 9 | const segment = segments[i] 10 | if (i === segments.length - 1) { 11 | current[segment] = paths[path] 12 | } else { 13 | if (!current[segment]) { 14 | current[segment] = Number.isNaN(Number(segments[i + 1])) ? {} : [] 15 | } 16 | current = current[segment] 17 | } 18 | } 19 | } 20 | 21 | return result 22 | } 23 | 24 | export function createPropsDefaultProxy( 25 | props: Record, 26 | defaults: Record, 27 | ): Record { 28 | const defaultProps = resolveDefaultProps(defaults) 29 | const result: Record = {} 30 | 31 | for (const key of [ 32 | ...new Set([...Object.keys(props), ...Object.keys(defaultProps)]), 33 | ]) { 34 | Object.defineProperty(result, key, { 35 | enumerable: true, 36 | get: () => (props[key] === undefined ? defaultProps[key] : props[key]), 37 | }) 38 | } 39 | 40 | return result 41 | } 42 | -------------------------------------------------------------------------------- /playground/src/for.tsx: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default () => { 4 | const count = ref(3) 5 | 6 | const Arr = ( 7 | <> 8 | {Array.from({ length: count.value }).map((_, index) => { 9 | if (index > 1) { 10 | return ( 11 | <> 12 |
({index}) lg 1
13 | 14 | ) 15 | } else { 16 | return [({index}) lt 1,
] 17 | } 18 | })} 19 | 20 | ) 21 | 22 | const selected = ref(0) 23 | return ( 24 |
25 | 26 | 27 |
28 |
29 | map 30 | {Arr} 31 |
32 | 33 |
34 | v-for 35 |
({ id })) 39 | } 40 | key={i.id} 41 | class={{ 'text-red': i.id === selected.value }} 42 | onClick={() => (selected.value = i.id)} 43 | > 44 | {i.id} 45 |
46 |
47 |
48 |
49 | ) 50 | } 51 | 52 | defineStyle(` 53 | .text-red { 54 | color: red; 55 | } 56 | `) 57 | -------------------------------------------------------------------------------- /packages/compiler-rs/crates/vapor/src/transform/v_html.rs: -------------------------------------------------------------------------------- 1 | use common::{error::ErrorCodes, expression::SimpleExpressionNode}; 2 | use napi::bindgen_prelude::{Either3, Either16}; 3 | use oxc_ast::ast::{JSXAttribute, JSXElement}; 4 | 5 | use crate::{ 6 | ir::index::{BlockIRNode, SetHtmlIRNode}, 7 | transform::{DirectiveTransformResult, TransformContext}, 8 | }; 9 | 10 | pub fn transform_v_html<'a>( 11 | dir: &'a mut JSXAttribute<'a>, 12 | node: &JSXElement, 13 | context: &'a TransformContext<'a>, 14 | context_block: &'a mut BlockIRNode<'a>, 15 | ) -> Option> { 16 | let exp = if let Some(value) = &mut dir.value { 17 | SimpleExpressionNode::new(Either3::C(value), context.ir.borrow().source) 18 | } else { 19 | context.options.on_error.as_ref()(ErrorCodes::VHtmlNoExpression, dir.span); 20 | SimpleExpressionNode::default() 21 | }; 22 | 23 | if !node.children.is_empty() { 24 | context.options.on_error.as_ref()(ErrorCodes::VHtmlWithChildren, node.span); 25 | return None; 26 | } 27 | 28 | let element = context.reference(&mut context_block.dynamic); 29 | context.register_effect( 30 | context_block, 31 | context.is_operation(vec![&exp]), 32 | Either16::I(SetHtmlIRNode { 33 | set_html: true, 34 | element, 35 | value: exp, 36 | }), 37 | None, 38 | None, 39 | ); 40 | None 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/unit-test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Set node 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: 20.x 21 | 22 | - name: Setup 23 | run: npm i -g @antfu/ni 24 | 25 | - name: Install 26 | run: nci 27 | 28 | - name: Lint 29 | run: nr lint 30 | 31 | test: 32 | runs-on: ${{ matrix.os }} 33 | 34 | strategy: 35 | matrix: 36 | node: [20.x] 37 | os: 38 | - ubuntu-latest 39 | # windows-latest, TODO: hash-sum result different with linux 40 | - macos-latest 41 | 42 | fail-fast: false 43 | 44 | steps: 45 | - uses: actions/checkout@v3 46 | - name: Set node ${{ matrix.node }} 47 | uses: actions/setup-node@v3 48 | with: 49 | node-version: ${{ matrix.node }} 50 | 51 | - name: Setup 52 | run: npm i -g @antfu/ni 53 | 54 | - name: Install 55 | run: nci 56 | 57 | - name: Build 58 | run: nr build 59 | 60 | - name: Type Check 61 | run: nr typecheck 62 | 63 | - name: Test 64 | run: nr test 65 | 66 | - name: Docs Build 67 | run: nr docs:build 68 | -------------------------------------------------------------------------------- /playground/src/if.tsx: -------------------------------------------------------------------------------- 1 | import { defineVaporComponent, ref } from 'vue' 2 | 3 | export default defineVaporComponent(() => { 4 | const count = ref(1) 5 | 6 | const Foo = () =>
2
7 | 8 | return ( 9 |
10 | 11 | 12 | 13 |
14 |
15 | expression 16 |
17 | {count.value === 1 ? ( 18 |
{1}
19 | ) : count.value === 2 ? ( 20 | 21 | ) : count.value >= 3 ? ( 22 |
lg 3: {count.value}
23 | ) : ( 24 |
lt 0: {count.value}
25 | )} 26 |
27 |
28 | 29 |
30 | v-if 31 |
32 | {/* @ts-ignore */} 33 |
34 | {count.value} 35 |
36 | 37 |
= 3}>lg 3: {count.value}
38 |
lt 0: {count.value}
39 |
40 |
41 |
42 |
43 | ) 44 | defineStyle(` 45 | .foo { 46 | color: red; 47 | } 48 | `) 49 | }) 50 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/custom_directive.rs: -------------------------------------------------------------------------------- 1 | use compiler_rs::transform; 2 | use insta::assert_snapshot; 3 | 4 | #[test] 5 | fn basic() { 6 | let code = transform("
", None).code; 7 | assert_snapshot!(code); 8 | } 9 | 10 | #[test] 11 | fn binding_value() { 12 | let code = transform("
", None).code; 13 | assert_snapshot!(code); 14 | } 15 | 16 | #[test] 17 | fn static_parameters() { 18 | let code = transform("
", None).code; 19 | assert_snapshot!(code); 20 | } 21 | 22 | #[test] 23 | fn modifiers() { 24 | let code = transform("
", None).code; 25 | assert_snapshot!(code); 26 | } 27 | 28 | #[test] 29 | fn modifiers_with_binding() { 30 | let code = transform("
", None).code; 31 | assert_snapshot!(code); 32 | } 33 | 34 | #[test] 35 | fn static_argument_and_modifiers() { 36 | let code = transform("
", None).code; 37 | assert_snapshot!(code); 38 | } 39 | 40 | #[test] 41 | fn dynamic_argument() { 42 | let code = transform("
", None).code; 43 | assert_snapshot!(code); 44 | } 45 | 46 | #[test] 47 | fn component() { 48 | let code = transform( 49 | " 50 |
51 | 52 |
53 |
", 54 | None, 55 | ) 56 | .code; 57 | assert_snapshot!(code); 58 | } 59 | -------------------------------------------------------------------------------- /packages/compiler-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["zhiyuanzmj <260480378@qq.com>"] 3 | edition = "2024" 4 | name = "compiler-rs" 5 | version = "0.1.0" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | default = ["napi"] 12 | napi = [] 13 | 14 | [workspace] 15 | resolver = "3" 16 | members = ["crates/*"] 17 | 18 | [workspace.dependencies] 19 | napi = { version = "3.3.0", features = ["napi9"] } 20 | napi-derive = "3.3.0" 21 | 22 | oxc_parser = "0.99.0" 23 | oxc_ast = "0.99.0" 24 | oxc_allocator = "0.99.0" 25 | oxc_span = "0.99.0" 26 | oxc_ast_visit = "0.99.0" 27 | oxc_traverse = "0.99.0" 28 | oxc_semantic = "0.99.0" 29 | oxc_codegen = "0.99.0" 30 | phf = "0.13.1" 31 | indexmap = "2.12.0" 32 | 33 | vdom = { path = "crates/vdom" } 34 | vapor = { path = "crates/vapor" } 35 | common = { path = "crates/common" } 36 | 37 | [dependencies] 38 | napi = { workspace = true } 39 | napi-derive = { workspace = true } 40 | 41 | oxc_parser = { workspace = true } 42 | oxc_ast = { workspace = true } 43 | oxc_allocator = { workspace = true } 44 | oxc_span = { workspace = true } 45 | oxc_traverse = { workspace = true } 46 | oxc_semantic = { workspace = true } 47 | oxc_codegen = { workspace = true } 48 | 49 | common = { workspace = true } 50 | vapor = { workspace = true } 51 | 52 | [dev-dependencies] 53 | insta = "1.43.2" 54 | criterion = "0.7.0" 55 | 56 | [[bench]] 57 | name = "bench" 58 | harness = false 59 | 60 | [build-dependencies] 61 | napi-build = "2" 62 | 63 | [profile.release] 64 | lto = true 65 | codegen-units = 1 66 | strip = "symbols" 67 | -------------------------------------------------------------------------------- /packages/compiler-rs/crates/vapor/src/generate/dom.rs: -------------------------------------------------------------------------------- 1 | use oxc_ast::NONE; 2 | use oxc_ast::ast::Statement; 3 | use oxc_span::SPAN; 4 | 5 | use crate::generate::CodegenContext; 6 | use crate::ir::index::InsertNodeIRNode; 7 | 8 | pub fn gen_insert_node<'a>(oper: InsertNodeIRNode, context: &CodegenContext<'a>) -> Statement<'a> { 9 | let ast = &context.ast; 10 | let InsertNodeIRNode { 11 | parent, 12 | elements, 13 | anchor, 14 | .. 15 | } = oper; 16 | 17 | let mut arguments = ast.vec(); 18 | if elements.len() > 1 { 19 | arguments.push( 20 | ast 21 | .expression_array( 22 | SPAN, 23 | ast.vec_from_iter(elements.into_iter().map(|element| { 24 | ast 25 | .expression_identifier(SPAN, ast.atom(&format!("n{}", element))) 26 | .into() 27 | })), 28 | ) 29 | .into(), 30 | ); 31 | } else { 32 | arguments.push( 33 | ast 34 | .expression_identifier(SPAN, ast.atom(&format!("n{}", elements[0]))) 35 | .into(), 36 | ) 37 | } 38 | 39 | arguments.push( 40 | ast 41 | .expression_identifier(SPAN, ast.atom(&format!("n{parent}"))) 42 | .into(), 43 | ); 44 | 45 | if let Some(anchor) = anchor { 46 | arguments.push( 47 | ast 48 | .expression_identifier(SPAN, ast.atom(&format!("n{anchor}"))) 49 | .into(), 50 | ); 51 | } 52 | 53 | ast.statement_expression( 54 | SPAN, 55 | ast.expression_call( 56 | SPAN, 57 | ast.expression_identifier(SPAN, ast.atom("insert")), 58 | NONE, 59 | arguments, 60 | false, 61 | ), 62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /packages/compiler-rs/crates/vapor/src/generate/v_show.rs: -------------------------------------------------------------------------------- 1 | use oxc_ast::NONE; 2 | use oxc_ast::ast::FormalParameterKind; 3 | use oxc_ast::ast::Statement; 4 | use oxc_span::SPAN; 5 | 6 | use crate::generate::CodegenContext; 7 | use crate::generate::expression::gen_expression; 8 | use crate::ir::index::DirectiveIRNode; 9 | 10 | pub fn gen_v_show<'a>(oper: DirectiveIRNode<'a>, context: &'a CodegenContext<'a>) -> Statement<'a> { 11 | let ast = &context.ast; 12 | let DirectiveIRNode { dir, element, .. } = oper; 13 | 14 | ast.statement_expression( 15 | SPAN, 16 | ast.expression_call( 17 | SPAN, 18 | ast.expression_identifier(SPAN, ast.atom(&context.helper("applyVShow"))), 19 | NONE, 20 | ast.vec_from_array([ 21 | ast 22 | .expression_identifier(SPAN, ast.atom(&format!("n{element}"))) 23 | .into(), 24 | ast 25 | .expression_arrow_function( 26 | SPAN, 27 | true, 28 | false, 29 | NONE, 30 | ast.formal_parameters( 31 | SPAN, 32 | FormalParameterKind::ArrowFormalParameters, 33 | ast.vec(), 34 | NONE, 35 | ), 36 | NONE, 37 | ast.function_body( 38 | SPAN, 39 | ast.vec(), 40 | ast.vec1( 41 | ast.statement_expression( 42 | SPAN, 43 | gen_expression(dir.exp.unwrap(), context, None, None), 44 | ), 45 | ), 46 | ), 47 | ) 48 | .into(), 49 | ]), 50 | false, 51 | ), 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /packages/macros/src/raw.ts: -------------------------------------------------------------------------------- 1 | import { createFilter, normalizePath } from '@vue-macros/common' 2 | import { transformJsxMacros } from './core' 3 | import { 4 | helperPrefix, 5 | useModelHelperCode, 6 | useModelHelperId, 7 | withDefaultsHelperCode, 8 | withDefaultsHelperId, 9 | } from './core/helper' 10 | import { transformStyle } from './core/style' 11 | import { resolveOptions, type Options } from './options' 12 | import type { UnpluginOptions } from 'unplugin' 13 | 14 | const name = '@vue-jsx-vapor/macros' 15 | 16 | const plugin = (userOptions: Options = {}): UnpluginOptions => { 17 | const options = resolveOptions(userOptions) 18 | const filter = createFilter(options) 19 | const importMap = new Map() 20 | 21 | return { 22 | name, 23 | enforce: 'pre', 24 | 25 | resolveId(id) { 26 | if (normalizePath(id).startsWith(helperPrefix)) return id 27 | }, 28 | loadInclude(id) { 29 | return normalizePath(id).startsWith(helperPrefix) 30 | }, 31 | load(_id) { 32 | const id = normalizePath(_id) 33 | if (id === useModelHelperId) return useModelHelperCode 34 | else if (id === withDefaultsHelperId) return withDefaultsHelperCode 35 | else if (importMap.get(id)) return importMap.get(id) 36 | }, 37 | 38 | transformInclude(id) { 39 | if (importMap.get(id)) return true 40 | return filter(id) 41 | }, 42 | transform(code, id, opt?: { ssr?: boolean }) { 43 | if (opt?.ssr) { 44 | options.defineComponent.autoReturnFunction = true 45 | } 46 | if (importMap.get(id)) return transformStyle(code, id, options) 47 | return transformJsxMacros(code, id, importMap, options) 48 | }, 49 | } 50 | } 51 | export default plugin 52 | -------------------------------------------------------------------------------- /packages/compiler-rs/wasi-worker.mjs: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import { createRequire } from "node:module"; 3 | import { parse } from "node:path"; 4 | import { WASI } from "node:wasi"; 5 | import { parentPort, Worker } from "node:worker_threads"; 6 | 7 | const require = createRequire(import.meta.url); 8 | 9 | const { instantiateNapiModuleSync, MessageHandler, getDefaultContext } = require("@napi-rs/wasm-runtime"); 10 | 11 | if (parentPort) { 12 | parentPort.on("message", (data) => { 13 | globalThis.onmessage({ data }); 14 | }); 15 | } 16 | 17 | Object.assign(globalThis, { 18 | self: globalThis, 19 | require, 20 | Worker, 21 | importScripts: function (f) { 22 | ;(0, eval)(fs.readFileSync(f, "utf8") + "//# sourceURL=" + f); 23 | }, 24 | postMessage: function (msg) { 25 | if (parentPort) { 26 | parentPort.postMessage(msg); 27 | } 28 | }, 29 | }); 30 | 31 | const emnapiContext = getDefaultContext(); 32 | 33 | const __rootDir = parse(process.cwd()).root; 34 | 35 | const handler = new MessageHandler({ 36 | onLoad({ wasmModule, wasmMemory }) { 37 | const wasi = new WASI({ 38 | version: 'preview1', 39 | env: process.env, 40 | preopens: { 41 | [__rootDir]: __rootDir, 42 | }, 43 | }); 44 | 45 | return instantiateNapiModuleSync(wasmModule, { 46 | childThread: true, 47 | wasi, 48 | context: emnapiContext, 49 | overwriteImports(importObject) { 50 | importObject.env = { 51 | ...importObject.env, 52 | ...importObject.napi, 53 | ...importObject.emnapi, 54 | memory: wasmMemory 55 | }; 56 | }, 57 | }); 58 | }, 59 | }); 60 | 61 | globalThis.onmessage = function (e) { 62 | handler.handle(e); 63 | }; 64 | -------------------------------------------------------------------------------- /packages/vue-jsx-vapor/jsx-runtime/index.cjs: -------------------------------------------------------------------------------- 1 | //#region rolldown:runtime 2 | const __create = Object.create 3 | const __defProp = Object.defineProperty 4 | const __getOwnPropDesc = Object.getOwnPropertyDescriptor 5 | const __getOwnPropNames = Object.getOwnPropertyNames 6 | const __getProtoOf = Object.getPrototypeOf 7 | const __hasOwnProp = Object.prototype.hasOwnProperty 8 | const __copyProps = (to, from, except, desc) => { 9 | if ((from && typeof from === 'object') || typeof from === 'function') 10 | for ( 11 | let keys = __getOwnPropNames(from), i = 0, n = keys.length, key; 12 | i < n; 13 | i++ 14 | ) { 15 | key = keys[i] 16 | if (!__hasOwnProp.call(to, key) && key !== except) 17 | __defProp(to, key, { 18 | get: ((k) => from[k]).bind(null, key), 19 | enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable, 20 | }) 21 | } 22 | return to 23 | } 24 | const __toESM = (mod, isNodeMode, target) => ( 25 | (target = mod != null ? __create(__getProtoOf(mod)) : {}), 26 | __copyProps( 27 | isNodeMode || !mod || !mod.__esModule 28 | ? __defProp(target, 'default', { 29 | value: mod, 30 | enumerable: true, 31 | }) 32 | : target, 33 | mod, 34 | ) 35 | ) 36 | //#endregion 37 | const vue = __toESM(require('vue')) 38 | const vue_jsx_vapor = __toESM(require('vue-jsx-vapor')) 39 | 40 | function jsx(type, props, key) { 41 | const { children, 'v-slots': vSlots } = props 42 | delete props.children 43 | delete props['v-slots'] 44 | if (arguments.length > 2) props.key = key 45 | return (0, vue_jsx_vapor.h)(type, props, vSlots || children) 46 | } 47 | 48 | exports.Fragment = vue.Fragment 49 | exports.jsx = jsx 50 | exports.jsxDEV = jsx 51 | exports.jsxs = jsx 52 | -------------------------------------------------------------------------------- /packages/macros/src/core/utils.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ArrowFunctionExpression, 3 | FunctionDeclaration, 4 | FunctionExpression, 5 | Node, 6 | } from '@babel/types' 7 | import type { MagicStringAST } from '@vue-macros/common' 8 | 9 | export type FunctionalNode = 10 | | FunctionDeclaration 11 | | FunctionExpression 12 | | ArrowFunctionExpression 13 | 14 | export function prependFunctionalNode( 15 | node: FunctionalNode, 16 | s: MagicStringAST, 17 | result: string, 18 | ): void { 19 | const isBlockStatement = node.body.type === 'BlockStatement' 20 | const start = node.body.extra?.parenthesized 21 | ? (node.body.extra.parenStart as number) 22 | : node.body.start! 23 | s.appendRight( 24 | start + (isBlockStatement ? 1 : 0), 25 | `${result};${!isBlockStatement ? 'return ' : ''}`, 26 | ) 27 | if (!isBlockStatement) { 28 | s.appendLeft(start, '{') 29 | s.appendRight(node.end!, '}') 30 | } 31 | } 32 | 33 | export function isFunctionalNode(node?: Node | null): node is FunctionalNode { 34 | return !!( 35 | node && 36 | (node.type === 'ArrowFunctionExpression' || 37 | node.type === 'FunctionDeclaration' || 38 | node.type === 'FunctionExpression') 39 | ) 40 | } 41 | 42 | export function getParamsStart(node: FunctionalNode, code: string): number { 43 | return node.params[0] 44 | ? node.params[0].start! 45 | : node.start! + 46 | (code.slice(node.start!, node.body.start!).match(/\(\s*\)/)?.index || 47 | 0) + 48 | 1 49 | } 50 | 51 | export function getDefaultValue(node: Node): Node { 52 | if (node.type === 'TSNonNullExpression') { 53 | return getDefaultValue(node.expression) 54 | } 55 | if (node.type === 'TSAsExpression') { 56 | return getDefaultValue(node.expression) 57 | } 58 | return node 59 | } 60 | -------------------------------------------------------------------------------- /packages/compiler-rs/compiler-rs.wasi-browser.js: -------------------------------------------------------------------------------- 1 | import { 2 | createOnMessage as __wasmCreateOnMessageForFsProxy, 3 | getDefaultContext as __emnapiGetDefaultContext, 4 | instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync, 5 | WASI as __WASI, 6 | } from '@napi-rs/wasm-runtime' 7 | 8 | 9 | 10 | const __wasi = new __WASI({ 11 | version: 'preview1', 12 | }) 13 | 14 | const __wasmUrl = new URL('./compiler-rs.wasm32-wasi.wasm', import.meta.url).href 15 | const __emnapiContext = __emnapiGetDefaultContext() 16 | 17 | 18 | const __sharedMemory = new WebAssembly.Memory({ 19 | initial: 4000, 20 | maximum: 65536, 21 | shared: true, 22 | }) 23 | 24 | const __wasmFile = await fetch(__wasmUrl).then((res) => res.arrayBuffer()) 25 | 26 | const { 27 | instance: __napiInstance, 28 | module: __wasiModule, 29 | napiModule: __napiModule, 30 | } = __emnapiInstantiateNapiModuleSync(__wasmFile, { 31 | context: __emnapiContext, 32 | asyncWorkPoolSize: 4, 33 | wasi: __wasi, 34 | onCreateWorker() { 35 | const worker = new Worker(new URL('./wasi-worker-browser.mjs', import.meta.url), { 36 | type: 'module', 37 | }) 38 | 39 | return worker 40 | }, 41 | overwriteImports(importObject) { 42 | importObject.env = { 43 | ...importObject.env, 44 | ...importObject.napi, 45 | ...importObject.emnapi, 46 | memory: __sharedMemory, 47 | } 48 | return importObject 49 | }, 50 | beforeInit({ instance }) { 51 | for (const name of Object.keys(instance.exports)) { 52 | if (name.startsWith('__napi_register__')) { 53 | instance.exports[name]() 54 | } 55 | } 56 | }, 57 | }) 58 | export default __napiModule.exports 59 | export const ErrorCodes = __napiModule.exports.ErrorCodes 60 | export const transform = __napiModule.exports.transform 61 | -------------------------------------------------------------------------------- /packages/macros/tests/fixtures.spec.ts: -------------------------------------------------------------------------------- 1 | import process from 'node:process' 2 | import { testFixtures } from '@vue-macros/test-utils' 3 | import { describe } from 'vitest' 4 | import { transformJsxMacros } from '../src/core' 5 | 6 | const options = { 7 | defineModel: { alias: ['defineModel'] }, 8 | defineSlots: { alias: ['defineSlots'] }, 9 | defineStyle: { alias: ['defineStyle'] }, 10 | defineExpose: { alias: ['defineExpose'] }, 11 | defineComponent: { alias: ['defineComponent', 'defineVaporComponent'] }, 12 | } 13 | 14 | // TODO: hash-sum's result is different on Windows and Linux 15 | const globs = 16 | process.platform === 'win32' 17 | ? import.meta.glob( 18 | ['./fixtures/**/*.tsx', '!./fixtures/**/define-style.tsx'], 19 | { 20 | eager: true, 21 | as: 'raw', 22 | }, 23 | ) 24 | : import.meta.glob('./fixtures/**/*.tsx', { 25 | eager: true, 26 | as: 'raw', 27 | }) 28 | 29 | describe('fixtures', async () => { 30 | await testFixtures( 31 | globs, 32 | (args, id, code) => 33 | transformJsxMacros(code, id, new Map(), { 34 | include: ['*.tsx'], 35 | version: 3.6, 36 | ...options, 37 | })?.code, 38 | ) 39 | }) 40 | 41 | describe('defineComponent autoReturnFunction fixtures', async () => { 42 | await testFixtures( 43 | import.meta.glob('./fixtures/**/define-component.tsx', { 44 | eager: true, 45 | as: 'raw', 46 | }), 47 | (args, id, code) => 48 | transformJsxMacros(code, id, new Map(), { 49 | include: ['*.tsx'], 50 | version: 3.6, 51 | ...options, 52 | defineComponent: { 53 | alias: ['defineComponent', 'defineVaporComponent'], 54 | autoReturnFunction: true, 55 | }, 56 | })?.code, 57 | ) 58 | }) 59 | -------------------------------------------------------------------------------- /docs/zh/introduction/eslint.md: -------------------------------------------------------------------------------- 1 | # ESLint 2 | 3 | 用于给 `vue-jsx-vapor` 自动格式化代码的 ESLint 插件。 4 | 5 | ## 安装 6 | 7 | ```sh 8 | pnpm add @vue-jsx-vapor/eslint 9 | ``` 10 | 11 | ## 配置 12 | 13 | ```ts 14 | // eslint.config.ts 15 | import vueJsxVapor from '@vue-jsx-vapor/eslint' 16 | 17 | export default [ 18 | vueJsxVapor() 19 | ] 20 | ``` 21 | 22 | ## define-style 23 | 24 | 使用 `prettier` 来格式化 `defineStyle` 宏中的样式。 25 | 26 | ```ts twoslash 27 | import vueJsxVapor from '@vue-jsx-vapor/eslint' 28 | 29 | export default [ 30 | vueJsxVapor({ 31 | rules: { 32 | 'vue-jsx-vapor/define-style': ['error', { tabWidth: 2 }] 33 | } 34 | }) 35 | ] 36 | ``` 37 | 38 | ## jsx-sort-props 39 | 40 | 这是 [@stylistic/jsx/jsx-sort-props](https://eslint.style/rules/jsx/jsx-sort-props) 的修改版,支持自定义 `reservedFirst` 和 `reservedLast` 选项。 41 | 42 | ```ts twoslash 43 | import vueJsxVapor from '@vue-jsx-vapor/eslint' 44 | 45 | export default [ 46 | vueJsxVapor({ 47 | rules: { 48 | 'vue-jsx-vapor/jsx-sort-props': ['error', { 49 | reservedFirst: ['v-if', 'v-for'], 50 | reservedLast: ['v-slot'], 51 | }] 52 | } 53 | }) 54 | ] 55 | ``` 56 | 57 | ### `reservedFirst` 58 | 59 | 默认为 `['v-if', 'v-else-if', 'v-else', 'v-for', 'key', 'ref', 'v-model']` 60 | 61 | 如果提供一个数组,数组中的值将覆盖默认的保留 props 列表。 62 | 这些 props 将遵循数组中指定的顺序: 63 | 64 | ```jsx 65 | // 转换前 66 | const Before = 67 | 68 | // 转换后 69 | const After = 70 | ``` 71 | 72 | ### `reservedLast` 73 | 74 | 默认为 `['v-slot', 'v-slots', 'v-text', 'v-html']` 75 | 76 | 这是一个数组选项。这些 props 必须在所有其他 props 之后列出。 77 | 它们将遵循数组中指定的顺序: 78 | 79 | ```jsx 80 | // 转换前 81 | const Before = 82 | 83 | // 转换后 84 | const After = 85 | ``` 86 | -------------------------------------------------------------------------------- /packages/macros/tests/fixtures/define-component.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, defineVaporComponent, nextTick, unref, VaporComponentInstance, VNode } from 'vue' 2 | 3 | const $ = unref 4 | 5 | const Comp = defineVaporComponent( 6 | ({ bar = 'bar'! as string, Comp, ...attrs }: { bar: string; baz: 'baz', Comp: any }) => { 7 | defineModel() 8 | const foo = $( 9 | defineModel('foo', { 10 | validator: (value) => { 11 | return value === 'foo' 12 | }, 13 | type: String, 14 | })!, 15 | ) 16 | return
17 | {[foo, bar, attrs.baz]} 18 | 19 |
20 | }, 21 | { name: 'Comp', props: { Comp: Object } }, 22 | ) 23 | 24 | const Comp1 = defineVaporComponent((props: { bar: 'bar'; 'onUpdate:bar': any, comp: any }) => { 25 | const foo = defineModel('foo') 26 | return
27 | {[foo.value, props['bar'], props['onUpdate:bar']]} 28 | 29 |
30 | }) 31 | 32 | const Comp2 = defineComponent(async () => { 33 | await nextTick() 34 | let foo = await new Promise((resolve) => { 35 | setTimeout(() => resolve('foo'), 1000) 36 | }) 37 | return () =>
{foo}
38 | }) 39 | 40 | const foo = () => {} 41 | defineVaporComponent(({ 42 | a = 0, 43 | b = 'b', 44 | c = true, 45 | d = () => {}, 46 | e = {}, 47 | f = [], 48 | g = foo, 49 | h = null, 50 | i = undefined!, 51 | }) => { 52 | return ( 53 | <> 54 | {a} 55 | {b} 56 | {c} 57 | {d} 58 | {e} 59 | {f} 60 | {g} 61 | {h} 62 | {i} 63 | 64 | ) 65 | }) 66 | 67 | // #21 68 | const Comp3 = defineComponent(()=>{ 69 | return () =>
123
70 | }) 71 | ; == ({} as VNode) 72 | const Comp4 = defineVaporComponent(() => { 73 | return
123
74 | }) 75 | ; == ({} as VaporComponentInstance) -------------------------------------------------------------------------------- /docs/zh/introduction/getting-started.md: -------------------------------------------------------------------------------- 1 | # 快速上手 2 | 3 | `vue-jsx-vapor` 是 `vue-jsx` 的 Vapor 模式。它支持 Vue 的所有指令和大部分宏。 4 | 5 | 在继续之前,我们假设您已经熟悉 Vue 的基本用法。 6 | 7 | ## 环境要求 8 | 9 | - Vue `>= v3.6`。 10 | - VSCode 扩展 [TS Macro](https://marketplace.visualstudio.com/items?itemName=zhiyuanzmj.vscode-ts-macro) 并且需要安装 `@ts-macro/tsc` 来替代 `tsc` 进行类型检查。 11 | ```json 12 | // package.json 13 | { 14 | "scripts": { 15 | "typecheck": "tsmc --noEmit" 16 | // ... 17 | } 18 | } 19 | ``` 20 | 21 | ## 安装 22 | 23 | ```bash [pnpm] 24 | # 插件 25 | pnpm add vue-jsx-vapor 26 | 27 | # 运行时 28 | pnpm add vue@3.6.0-alpha.2 29 | ``` 30 | 31 | ## 配置 32 | 33 | ::: code-group 34 | 35 | ```ts [vite.config.ts] 36 | import { defineConfig } from 'vite' 37 | import vueJsxVapor from 'vue-jsx-vapor/vite' 38 | 39 | export default defineConfig({ 40 | plugins: [ 41 | vueJsxVapor({ 42 | macros: true, 43 | }), 44 | ], 45 | }) 46 | ``` 47 | 48 | ::: 49 | 50 | ## Typescript 51 | 52 | ### 配置 `tsconfig.json` 53 | ```json 54 | { 55 | "compilerOptions": { 56 | "jsx": "preserve", 57 | "jsxImportSource": "vue-jsx-vapor", 58 | // ... 59 | } 60 | } 61 | ``` 62 | 63 | ### Volar 插件 64 | 65 | 由于 `vue-jsx-vapor` 支持 Vue 指令和 Vue 宏,所以需要安装 [TS Macro](https://marketplace.visualstudio.com/items?itemName=zhiyuanzmj.vscode-ts-macro) 的 VSCode 插件来加载 `vue-jsx-vapor/volar` 插件, 以获得类型支持。 66 | 67 | `TS Macro` 的 VSCode 会通过分析 `vite.config.ts` 来自动加载 `vue-jsx-vapor/volar` 插件并共享 `vue-jsx-vapor/vite` 插件的用户配置,无需手动配置 `ts-macro.config.ts`。 68 | 69 | 70 | ::: details 手动配置 71 | 72 | ::: code-group 73 | 74 | ```ts [ts-macro.config.ts] 75 | import vueJsxVapor from 'vue-jsx-vapor/volar' 76 | 77 | export default { 78 | plugins: [ 79 | vueJsxVapor({ 80 | macros: true, 81 | }), 82 | ], 83 | } 84 | ``` 85 | 86 | ::: 87 | 88 | ## 模板 89 | 90 | - [vitesse-jsx-vapor](https://github.com/zhiyuanzmj/vitesse-jsx-vapor) 91 | -------------------------------------------------------------------------------- /packages/compiler-rs/tests/vapor/transform_text.rs: -------------------------------------------------------------------------------- 1 | use compiler_rs::transform; 2 | use insta::assert_snapshot; 3 | 4 | #[test] 5 | fn static_template() { 6 | let code = transform( 7 | "
8 |
hello
9 | 10 | 11 |
", 12 | None, 13 | ) 14 | .code; 15 | assert_snapshot!(code); 16 | } 17 | 18 | #[test] 19 | fn interpolation() { 20 | let code = transform("<>{ 1 }{ 2 }{a +b + c }", None).code; 21 | assert_snapshot!(code); 22 | } 23 | 24 | #[test] 25 | fn on_consecutive_text() { 26 | let code = transform("<>{ \"hello world\" }", None).code; 27 | assert_snapshot!(code); 28 | } 29 | 30 | #[test] 31 | fn consecutive_text() { 32 | let code = transform("<>{ msg }", None).code; 33 | assert_snapshot!(code); 34 | } 35 | 36 | #[test] 37 | fn escapes_raw_static_text_when_generating_the_template_string() { 38 | let code = transform("<script>", None).code; 39 | assert_snapshot!(code); 40 | } 41 | 42 | #[test] 43 | fn text_like() { 44 | let code = transform("
{ (2) }{`foo${1}`}{1}{1n}
", None).code; 45 | assert_snapshot!(code); 46 | } 47 | 48 | #[test] 49 | fn expression_conditional() { 50 | let code = transform( 51 | "<>{ok? ({msg}) : fail ? (
fail
) : null }", 52 | None, 53 | ) 54 | .code; 55 | assert_snapshot!(code); 56 | } 57 | 58 | #[test] 59 | fn expression_logical() { 60 | let code = transform("<>{ok && (
{msg}
)}", None).code; 61 | assert_snapshot!(code); 62 | } 63 | 64 | #[test] 65 | fn expression_map() { 66 | let code = transform( 67 | "<>{Array.from({ length: count.value }).map((_, index) => { 68 | if (index > 1) { 69 | return
1
70 | } else { 71 | return [({index}) lt 1,
] 72 | } 73 | })}", 74 | None, 75 | ) 76 | .code; 77 | assert_snapshot!(code); 78 | } 79 | -------------------------------------------------------------------------------- /playground/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { defineVaporComponent, ref, type Ref } from 'vue' 2 | import { useRef } from 'vue-jsx-vapor' 3 | import VueComp from './Comp.vue' 4 | import Count2 from './count' 5 | import For from './for' 6 | import Html from './html' 7 | import If from './if' 8 | import Model from './model' 9 | import Once from './once' 10 | import Show from './show' 11 | import Slot from './slot' 12 | 13 | export default defineVaporComponent(() => { 14 | const count = ref('1') 15 | 16 | const Count = (props: { value: string }) => { 17 | return
{props.value}
18 | } 19 | 20 | const Count1 = ({ value }: { value: Ref }) => { 21 | return
{value.value}
22 | } 23 | 24 | const compRef = useRef() 25 | 26 | return ( 27 | <> 28 |
29 | 30 | (count.value = e.currentTarget.value)} 33 | /> 34 | 35 | 36 | 37 | 38 | {compRef.value?.double} 39 |
40 | 41 |
42 | v-if 43 | 44 |
45 | 46 |
47 | v-for 48 | 49 |
50 | 51 |
52 | v-slot 53 | 54 |
55 | 56 |
57 | v-model 58 | 59 |
60 | 61 |
62 | v-show 63 | 64 |
65 | 66 |
67 | v-html 68 | 69 |
70 | 71 |
72 | v-once 73 | 74 |
75 | 76 | ) 77 | }) 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3.0.4", 3 | "packageManager": "pnpm@10.17.0", 4 | "description": "Vue JSX Vapor", 5 | "type": "module", 6 | "keywords": [ 7 | "unplugin", 8 | "vite", 9 | "webpack", 10 | "rollup", 11 | "transform", 12 | "vue-jsx", 13 | "volar", 14 | "vapor", 15 | "babel", 16 | "compiler" 17 | ], 18 | "license": "MIT", 19 | "homepage": "https://github.com/vuejs/vue-jsx-vapor#readme", 20 | "bugs": { 21 | "url": "https://github.com/vuejs/vue-jsx-vapor/issues" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/vuejs/vue-jsx-vapor.git" 26 | }, 27 | "scripts": { 28 | "dev": "pnpm run --filter=\"./packages/*\" --parallel dev", 29 | "build": "pnpm run --filter=\"./packages/*\" --parallel build", 30 | "bench": "pnpm run -C ./benchmark bench", 31 | "typecheck": "tsmc --noEmit", 32 | "lint": "eslint .", 33 | "play": "npm -C playground run dev", 34 | "test": "vitest", 35 | "test-rs": "pnpm run --filter=\"compiler-rs\" test", 36 | "release": "bumpp -r --all -x 'pnpm run changelog'", 37 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", 38 | "docs:dev": "pnpm run -C ./docs dev", 39 | "docs:preview": "pnpm run -C ./docs preview", 40 | "docs:build": "pnpm run -C ./docs build" 41 | }, 42 | "devDependencies": { 43 | "@sxzz/eslint-config": "^7.1.4", 44 | "@ts-macro/tsc": "catalog:", 45 | "@types/node": "^22.18.6", 46 | "bumpp": "^10.2.3", 47 | "conventional-changelog-cli": "^5.0.0", 48 | "eslint": "^9.36.0", 49 | "tsdown": "^0.15.12", 50 | "typescript": "^5.9.2", 51 | "unplugin-raw": "^0.5.1", 52 | "vite": "catalog:", 53 | "vitest": "catalog:", 54 | "vue-jsx-vapor": "workspace:*" 55 | }, 56 | "pnpm": { 57 | "overrides": { 58 | "estree-walker": "2.0.2", 59 | "ts-macro": "catalog:" 60 | } 61 | } 62 | } 63 | --------------------------------------------------------------------------------