Skip to content

Commit

Permalink
feat: introduce definePropsRefs
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz committed Jan 27, 2023
1 parent 33849ad commit c91cb0f
Show file tree
Hide file tree
Showing 35 changed files with 450 additions and 2 deletions.
10 changes: 10 additions & 0 deletions .changeset/many-crabs-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@vue-macros/common': major
'@vue-macros/nuxt': major
'@vue-macros/define-props-refs': minor
'unplugin-vue-macros': minor
'@vue-macros/volar': minor
'@vue-macros/define-props': patch
---

introduce `definePropsRefs`
4 changes: 4 additions & 0 deletions docs/.vitepress/configs/navs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export const sidebar: DefaultTheme.Sidebar = [
text: 'defineProps',
link: '/macros/define-props',
},
{
text: 'definePropsRefs',
link: '/macros/define-props-refs',
},
{
text: 'defineSlots',
link: '/macros/define-slots',
Expand Down
1 change: 1 addition & 0 deletions docs/guide/bundler-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ npm i -D @vue-macros/volar
"plugins": [
"@vue-macros/volar/define-model",
"@vue-macros/volar/define-props",
"@vue-macros/volar/define-props-refs",
"@vue-macros/volar/short-vmodel",
"@vue-macros/volar/define-slots",
"@vue-macros/volar/export-props"
Expand Down
43 changes: 43 additions & 0 deletions docs/macros/define-props-refs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# definePropsRefs

<small>Stability: <code class="!text-yellow-600">unstable</code></small>

Returns refs from `defineProps` instead of a reactive object. It can be destructured without losing reactivity.

`toRefs(defineProps())` => `definePropsRefs()`

| Features | Supported |
| :-----------------------: | :----------------: |
| Vue 3 | :white_check_mark: |
| Nuxt 3 | :white_check_mark: |
| Vue 2 | :white_check_mark: |
| TypeScript / Volar Plugin | :white_check_mark: |

## Basic Usage

```vue {2-3,8}
<script setup lang="ts">
// ✅ won't lose reactivity with destructuring
const { foo, bar } = definePropsRefs<{
foo: string
bar: number
}>()
// ⬇️ Ref<string>
console.log(foo.value, bar.value)
</script>
```

## Volar Configuration

```jsonc {6}
// tsconfig.json
{
"vueCompilerOptions": {
"target": 3,
"plugins": [
"@vue-macros/volar/define-props-refs"
// ...more feature
]
}
}
```
2 changes: 1 addition & 1 deletion docs/macros/define-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Unfortunately Reactivity Transform is not implemented in Vue 2, so this macro do
## Basic Usage

```vue
<script setup lang="tsx">
<script setup lang="ts">
// ⬇️ ReactiveVariable<string[]>
const { foo } = $defineProps<{
foo: string[]
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const DEFINE_PROPS = 'defineProps'
export const DEFINE_PROPS_DOLLAR = '$defineProps'
export const DEFINE_PROPS_REFS = 'definePropsRefs'
export const DEFINE_EMITS = 'defineEmits'
export const WITH_DEFAULTS = 'withDefaults'
export const DEFINE_OPTIONS = 'defineOptions'
Expand Down
3 changes: 3 additions & 0 deletions packages/define-props-refs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @vue-macros/define-props-refs [![npm](https://img.shields.io/npm/v/@vue-macros/define-props-refs.svg)](https://npmjs.com/package/@vue-macros/define-props-refs)

Please refer to [README.md](https://github.com/sxzz/unplugin-vue-macros#readme)
5 changes: 5 additions & 0 deletions packages/define-props-refs/macros-global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { definePropsRefs as _definePropsRefs } from './macros'

declare global {
const definePropsRefs: typeof _definePropsRefs
}
20 changes: 20 additions & 0 deletions packages/define-props-refs/macros.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type {
ComponentObjectPropsOptions,
DeepReadonly,
ExtractPropTypes,
Ref,
ToRefs,
} from 'vue'

export declare function definePropsRefs<PropNames extends string = string>(
props: PropNames[]
): DeepReadonly<{
[key in PropNames]: Readonly<Ref<any>>
}>
export declare function definePropsRefs<
PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions
>(props: PP): DeepReadonly<Required<ToRefs<ExtractPropTypes<PP>>>>

export declare function definePropsRefs<TypeProps>(): DeepReadonly<
Required<ToRefs<TypeProps>>
>
80 changes: 80 additions & 0 deletions packages/define-props-refs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"name": "@vue-macros/define-props-refs",
"version": "0.0.0",
"packageManager": "pnpm@7.26.0",
"description": "",
"keywords": [
"unplugin",
"vue",
"sfc",
"setup",
"macros",
"script-setup",
"define-props-refs"
],
"license": "MIT",
"homepage": "https://github.com/sxzz/unplugin-vue-macros#readme",
"bugs": {
"url": "https://github.com/sxzz/unplugin-vue-macros/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sxzz/unplugin-vue-macros.git"
},
"author": "三咲智子 <sxzz@sxzz.moe>",
"files": [
"dist",
"*.d.ts"
],
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./vite": {
"types": "./dist/vite.d.ts",
"require": "./dist/vite.js",
"import": "./dist/vite.mjs"
},
"./webpack": {
"types": "./dist/webpack.d.ts",
"require": "./dist/webpack.js",
"import": "./dist/webpack.mjs"
},
"./rollup": {
"types": "./dist/rollup.d.ts",
"require": "./dist/rollup.js",
"import": "./dist/rollup.mjs"
},
"./esbuild": {
"types": "./dist/esbuild.d.ts",
"require": "./dist/esbuild.js",
"import": "./dist/esbuild.mjs"
},
"./macros": "./macros.d.ts",
"./macros-global": "./macros-global.d.ts",
"./*": "./*"
},
"scripts": {
"build": "tsup && tsx ../../scripts/postbuild.mts",
"dev": "DEV=1 tsup"
},
"peerDependencies": {
"vue": "^2.7.0 || ^3.2.25"
},
"dependencies": {
"@rollup/pluginutils": "^5.0.2",
"@vue-macros/common": "workspace:~",
"unplugin": "^1.0.1"
},
"devDependencies": {
"vue": "^3.2.45"
},
"engines": {
"node": ">=14.19.0"
}
}
42 changes: 42 additions & 0 deletions packages/define-props-refs/src/core/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {
DEFINE_PROPS_REFS,
MagicString,
getTransformResult,
isCallOf,
parseSFC,
walkAST,
} from '@vue-macros/common'
import type { Node } from '@babel/types'

export function transformDefinePropsRefs(code: string, id: string) {
if (!code.includes(DEFINE_PROPS_REFS)) return

const { scriptSetup, getSetupAst } = parseSFC(code, id)
if (!scriptSetup) return

const offset = scriptSetup.loc.start.offset
const s = new MagicString(code)
const setupAst = getSetupAst()!

let changed = false
walkAST<Node>(setupAst, {
enter(node) {
if (!isCallOf(node, DEFINE_PROPS_REFS)) return
s.prependLeft(
offset,
`\nconst __MACROS_props = defineProps${s.slice(
offset + node.callee.end!,
offset + node.end!
)}`
)
s.overwriteNode(node, '_MACROS_toRefs(__MACROS_props)', { offset })
changed = true
},
})

if (changed) {
s.prependLeft(offset, `\nimport { toRefs as _MACROS_toRefs } from 'vue'`)
}

return getTransformResult(s, id)
}
3 changes: 3 additions & 0 deletions packages/define-props-refs/src/esbuild.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.esbuild
62 changes: 62 additions & 0 deletions packages/define-props-refs/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { createUnplugin } from 'unplugin'
import { createFilter } from '@rollup/pluginutils'
import {
REGEX_SETUP_SFC,
REGEX_VUE_SFC,
REGEX_VUE_SUB,
detectVueVersion,
} from '@vue-macros/common'
import { transformDefinePropsRefs } from './core'
import type { MarkRequired } from '@vue-macros/common'
import type { UnpluginContextMeta } from 'unplugin'
import type { FilterPattern } from '@rollup/pluginutils'

export { transformDefinePropsRefs } from './core'

export interface Options {
include?: FilterPattern
exclude?: FilterPattern
version?: 2 | 3
}

export type OptionsResolved = MarkRequired<Options, 'include' | 'version'>

function resolveOption(
options: Options,
framework: UnpluginContextMeta['framework']
): OptionsResolved {
const version = options.version || detectVueVersion()
return {
include: [REGEX_VUE_SFC, REGEX_SETUP_SFC].concat(
version === 2 && framework === 'webpack' ? REGEX_VUE_SUB : []
),
...options,
version,
}
}

const name = 'unplugin-vue-define-props-refs'

export default createUnplugin<Options | undefined, false>(
(userOptions = {}, { framework }) => {
const options = resolveOption(userOptions, framework)
const filter = createFilter(options.include, options.exclude)

return {
name,
enforce: 'pre',

transformInclude(id) {
return filter(id)
},

transform(code, id) {
try {
return transformDefinePropsRefs(code, id)
} catch (err: unknown) {
this.error(`${name} ${err}`)
}
},
}
}
)
3 changes: 3 additions & 0 deletions packages/define-props-refs/src/rollup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.rollup
3 changes: 3 additions & 0 deletions packages/define-props-refs/src/vite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.vite
3 changes: 3 additions & 0 deletions packages/define-props-refs/src/webpack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.webpack
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Vitest Snapshot v1

exports[`fixtures > ./fixtures/basic.vue 1`] = `
"<script setup lang=\\"ts\\">
import { toRefs as _MACROS_toRefs } from 'vue'
const __MACROS_props = defineProps<{
foo: string[]
bar: number
}>()
import { expectTypeOf } from 'expect-type'
import type { Ref } from 'vue'
const { foo, bar } = _MACROS_toRefs(__MACROS_props)
expectTypeOf(foo).toEqualTypeOf<Ref<string[]>>()
expectTypeOf(bar).toEqualTypeOf<Ref<number>>()
</script>
<template>
<div></div>
</template>
"
`;
13 changes: 13 additions & 0 deletions packages/define-props-refs/tests/fixtures.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { testFixtures } from '@vue-macros/test-utils'
import { describe } from 'vitest'
import { transformDefinePropsRefs } from '../src/core'

describe('fixtures', async () => {
await testFixtures(
import.meta.glob('./fixtures/*.{vue,js,ts}', {
eager: true,
as: 'raw',
}),
(args, id, code) => transformDefinePropsRefs(code, id)?.code
)
})
15 changes: 15 additions & 0 deletions packages/define-props-refs/tests/fixtures/basic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
import { expectTypeOf } from 'expect-type'
import type { Ref } from 'vue'
const { foo, bar } = definePropsRefs<{
foo: string[]
bar: number
}>()
expectTypeOf(foo).toEqualTypeOf<Ref<string[]>>()
expectTypeOf(bar).toEqualTypeOf<Ref<number>>()
</script>

<template>
<div></div>
</template>
3 changes: 3 additions & 0 deletions packages/define-props-refs/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import cfg from '../common/tsup.config.js'

export default cfg
2 changes: 2 additions & 0 deletions packages/define-props/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
},
"./macros": "./macros.d.ts",
"./macros-global": "./macros-global.d.ts",
"./vue2-macros": "./vue2-macros.d.ts",
"./vue2-macros-global": "./vue2-macros-global.d.ts",
"./*": "./*"
},
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions packages/macros/macros-global.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference types="@vue-macros/define-model/macros-global" />
/// <reference types="unplugin-vue-define-options/macros-global" />
/// <reference types="@vue-macros/define-props/macros-global" />
/// <reference types="@vue-macros/define-props-refs/macros-global" />
/// <reference types="@vue-macros/define-render/macros-global" />
/// <reference types="@vue-macros/define-slots/macros-global" />
/// <reference types="@vue-macros/reactivity-transform/macros-global" />
Expand Down

0 comments on commit c91cb0f

Please sign in to comment.