vue-macros icon indicating copy to clipboard operation
vue-macros copied to clipboard

feat: introduce jsx-macros

Open zhiyuanzmj opened this issue 1 year ago • 5 comments

Description

This PR provides five macros for functional component.

  1. defineComponent

    • Auto collects usage props to the props option.

      defineComponent((props: { 
        foo: string,
        bar: string        
      // ^ Unused prop as a fallthrough attribute will be automatically added to the root element's attributes. 
      }) => {
        return () => (
          <div>
            <span>{props.foo}</span>
          </div>
        )
      })
      

      Will be covert to

      defineComponent((props) => {
        return () => (
          <div>
            <span>{props.foo}</span>
          </div>
        )
      }, { props: { foo: null } })
      
    • The destructuring-props will be auto restructure and collects to the props option.

    • The rest prop will be convert to attrs, And the inheritAttrs option will defaults to false. By the way you can define the attrs type.

      defineComponent(({
        foo,
        ...attrs
      }: { foo: string, bar: string }) => {
        return () => (
          <div>
            <span {...attrs}>{foo}</span>
          </div>
        )
      })
      

      Will be covert to

      defineComponent((_props) => {
        const attrs = useAttrs()
        return () => (
          <div>
            <span {...attrs}>{_props.foo}</span>
          </div>
        )
      }, { inheritAttrs: false, props: { foo: null } })
      
    • Support await expression and getCurrentInstance() co-usage.

    • Support return JSX.

    • If the prop's default-value ends with !, The prop will be inferred as required.

    Usage

    defineComponent(<T,>({
      foo = undefined as T,
      barRequired = ''! 
      //              ^ specified the prop as required for Volar plugin.
    }) => {
      const modelValue = defineModel({ 
        validator: (value) => value === 'model',
        required: true 
      })
      return (
        <div class={props.class}>
          {[foo, modelValue.value]}
        </div>
      )
    })
    

    Will be convert to:

    defineComponent(<T,>(_props) => {
      const _default_props = createPropsDefaultProxy(_props, { 'barRequired': '' });
      const modelValue = useModel(_props, 'modelValue')
      return () => (
        <div>
          {[_props.foo, modelValue.value]}
        </div>
      )
    }, { 
      props: {
        foo: null, 
        barRequired: {
          required: true
        },
        modelValue: {
          validator: (value) => value === 'model',
          required: true
        }, 
        'onUpdate:modelValue': null, 
      }
    })
    
  2. defineModel

    • Don't support hyphenated modelName.
    • Modified model's value can be read synchronously, needn't await nextTick().
      Related issue: https://github.com/vuejs/core/issues/11080
    • Will be inferred as required prop when the expression ends with !.

    Usage

    function Comp(){
      const modelValue = defineModel<string>()!
      return <div />
    }
    // will be convert to 
    function Comp(props: { modelValue: string, 'onUpdate:modelValue': (e: string) => any  }){
      const modelValue = useModel<string>(props, 'modelValue', { required: true })
      return <div />
    }
    
  3. defineExpose just like in vue-setup.

    • Also support react & preact

    Setup

    // vue-macros.config.ts
    import { defineConfig } from 'unplugin-vue-macros'
    export default defineConfig({
      jsxMacros: {
         lib: 'react' // Or 'preact'
      },
      scriptSFC: true,
    })
    

    Usage

    const Comp = () => {
     const [foo] = useState()
     defineExpose({
       foo
     })
     return <div>{foo}</div>
    }
    // will be convert to:
    const Comp = forwardRef((props, _ref)=>{
     const [foo] = useState()
     useImperativeHandle(_ref, () => ({
       foo
     }),['foo'])
     return <div>{foo}</div>
    }
    
  4. defineSlots

    • If you use generic to define slots, all slots will be optional.
    const slots = defineSlots<{
      default: () => any
    }>()
    slots.default?.()
    //           ^ optional
    
    • Support default slots (Recommended).
    function Comp(){
      const slots = defineSlots({
        default: (props: {foo: number}) => <div>default slot: {props.foo}</div>
        title: (props: {foo: number}) => <div>title slot: {props.foo}</div>
      })
    
      return (
        <>
          <slots.default foo={1} />
          <slots.title foo={1} />
        </>
      )
    }
    
  5. defineStyle

    • Support defining multiple style macros in a file.

    • Supported css pre-processors: css | scss|sass|less|stylus|postcss.

      // default pre-processor is `css`.
      defineStyle(`
        .foo {
          color: red;
        }
      `)
      
      // use `scss` pre-processor.
      defineStyle.scss(``)
      
    • Support css modules, If the macro is an assignment expression.

      const { foo, bar } = defineStyle.scss(`
        .foo {
          color: blue;
          .bar {
            background: red;
          }
        }
      `)
      
    • Support scoped mode.

      • If defined at the top level of the file, The scoped option is false.
      • If defined within a function, The scoped option defaults to true.

      Usage

      defineStyle(`
        .foo {
           color: red;
         }
      `)  // The scoped option is false.
      
      function Comp(){
        defineStyle(`` {
          scoped: false // default is true, you can manually set to false.
        })
      }
      
    • Support CSS-variable and JS-variable binding.

      const Comp = () => {
        const color = ref('red')
        defineStyle.scss(`
          .foo {
            color: ${color.value};
          }
        `)
         return <div class="foo" />
      }
      

zhiyuanzmj avatar Sep 06 '24 16:09 zhiyuanzmj

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
vue-macros ✅ Ready (Inspect) Visit Preview Mar 14, 2025 2:11am

vercel[bot] avatar Sep 06 '24 16:09 vercel[bot]

🦋 Changeset detected

Latest commit: 22e745cd62303bb043928e30afd2b287c78e9cc8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 34 packages
Name Type
@vue-macros/jsx-macros Minor
unplugin-vue-macros Minor
@vue-macros/volar Minor
@vue-macros/config Patch
@vue-macros/common Patch
@vue-macros/astro Patch
@vue-macros/nuxt Patch
@vue-macros/api Patch
@vue-macros/better-define Patch
@vue-macros/boolean-prop Patch
@vue-macros/chain-call Patch
@vue-macros/define-emit Patch
@vue-macros/define-models Patch
unplugin-vue-define-options Patch
@vue-macros/define-prop Patch
@vue-macros/define-props-refs Patch
@vue-macros/define-props Patch
@vue-macros/define-render Patch
@vue-macros/define-slots Patch
@vue-macros/define-stylex Patch
@vue-macros/export-expose Patch
@vue-macros/export-props Patch
@vue-macros/export-render Patch
@vue-macros/hoist-static Patch
@vue-macros/jsx-directive Patch
@vue-macros/named-template Patch
@vue-macros/reactivity-transform Patch
@vue-macros/script-lang Patch
@vue-macros/setup-block Patch
@vue-macros/setup-component Patch
@vue-macros/setup-sfc Patch
@vue-macros/short-bind Patch
@vue-macros/short-emits Patch
@vue-macros/short-vmodel Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

changeset-bot[bot] avatar Sep 06 '24 16:09 changeset-bot[bot]

Open in Stackblitz

@vue-macros/api

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/api@794
@vue-macros/astro

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/astro@794
@vue-macros/better-define

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/better-define@794
@vue-macros/boolean-prop

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/boolean-prop@794
@vue-macros/chain-call

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/chain-call@794
@vue-macros/config

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/config@794
@vue-macros/common

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/common@794
@vue-macros/define-emit

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/define-emit@794
@vue-macros/define-models

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/define-models@794
unplugin-vue-define-options

npm i https://pkg.pr.new/vue-macros/vue-macros/unplugin-vue-define-options@794
@vue-macros/define-prop

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/define-prop@794
@vue-macros/define-props

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/define-props@794
@vue-macros/define-props-refs

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/define-props-refs@794
@vue-macros/define-render

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/define-render@794
@vue-macros/define-slots

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/define-slots@794
@vue-macros/define-stylex

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/define-stylex@794
@vue-macros/devtools

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/devtools@794
@vue-macros/eslint-config

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/eslint-config@794
@vue-macros/export-expose

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/export-expose@794
@vue-macros/export-props

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/export-props@794
@vue-macros/export-render

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/export-render@794
@vue-macros/hoist-static

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/hoist-static@794
@vue-macros/jsx-directive

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/jsx-directive@794
@vue-macros/jsx-macros

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/jsx-macros@794
vue-macros

npm i https://pkg.pr.new/vue-macros/vue-macros@794
@vue-macros/named-template

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/named-template@794
@vue-macros/nuxt

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/nuxt@794
@vue-macros/reactivity-transform

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/reactivity-transform@794
@vue-macros/script-lang

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/script-lang@794
@vue-macros/setup-block

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/setup-block@794
@vue-macros/setup-component

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/setup-component@794
@vue-macros/setup-sfc

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/setup-sfc@794
@vue-macros/short-bind

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/short-bind@794
@vue-macros/short-emits

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/short-emits@794
@vue-macros/short-vmodel

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/short-vmodel@794
@vue-macros/test-utils

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/test-utils@794
@vue-macros/volar

npm i https://pkg.pr.new/vue-macros/vue-macros/@vue-macros/volar@794

commit: 0bbb422

pkg-pr-new[bot] avatar Sep 06 '24 16:09 pkg-pr-new[bot]

I have merged #851 in advance. so that I can use pkg-pr-new. Please review #851 first.

zhiyuanzmj avatar Nov 08 '24 03:11 zhiyuanzmj