Skip to content

Commit

Permalink
feat(compiler-sfc): support defineAttrs macro
Browse files Browse the repository at this point in the history
  • Loading branch information
rudyxu1102 committed Oct 17, 2023
1 parent 2ffe3d5 commit 94a60f3
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`defineAttrs() > basic usage 1`] = `
"import { useAttrs as _useAttrs, defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({
setup(__props, { expose: __expose }) {
__expose();
const attrs = _useAttrs()
return { attrs }
}
})"
`;

exports[`defineAttrs() > w/o generic params 1`] = `
"import { useAttrs as _useAttrs } from 'vue'
export default {
setup(__props, { expose: __expose }) {
__expose();
const attrs = _useAttrs()
return { attrs }
}
}"
`;

exports[`defineAttrs() > w/o return value 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({
setup(__props, { expose: __expose }) {
__expose();
return { }
}
})"
`;
40 changes: 40 additions & 0 deletions packages/compiler-sfc/__tests__/compileScript/defineAttrs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { compileSFCScript as compile, assertCode } from '../utils'

describe('defineAttrs()', () => {
test('basic usage', () => {
const { content } = compile(`
<script setup lang="ts">
const attrs = defineAttrs<{
bar?: number
}>()
</script>
`)
assertCode(content)
expect(content).toMatch(`const attrs = _useAttrs()`)
expect(content).not.toMatch('defineAttrs')
})

test('w/o return value', () => {
const { content } = compile(`
<script setup lang="ts">
defineAttrs<{
bar?: number
}>()
</script>
`)
assertCode(content)
expect(content).not.toMatch('defineAttrs')
expect(content).not.toMatch(`_useAttrs`)
})

test('w/o generic params', () => {
const { content } = compile(`
<script setup>
const attrs = defineAttrs()
</script>
`)
assertCode(content)
expect(content).toMatch(`const attrs = _useAttrs()`)
expect(content).not.toMatch('defineAttrs')
})
})
7 changes: 5 additions & 2 deletions packages/compiler-sfc/src/compileScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
import { analyzeScriptBindings } from './script/analyzeScriptBindings'
import { isImportUsed } from './script/importUsageCheck'
import { processAwait } from './script/topLevelAwait'
import { processDefineAttrs } from './script/defineAttrs'

export interface SFCScriptCompileOptions {
/**
Expand Down Expand Up @@ -512,7 +513,8 @@ export function compileScript(
processDefineProps(ctx, expr) ||
processDefineEmits(ctx, expr) ||
processDefineOptions(ctx, expr) ||
processDefineSlots(ctx, expr)
processDefineSlots(ctx, expr) ||
processDefineAttrs(ctx, expr)
) {
ctx.s.remove(node.start! + startOffset, node.end! + startOffset)
} else if (processDefineExpose(ctx, expr)) {
Expand Down Expand Up @@ -550,7 +552,8 @@ export function compileScript(
!isDefineProps && processDefineEmits(ctx, init, decl.id)
!isDefineEmits &&
(processDefineSlots(ctx, init, decl.id) ||
processDefineModel(ctx, init, decl.id))
processDefineModel(ctx, init, decl.id) ||
processDefineAttrs(ctx, init, decl.id))

if (
isDefineProps &&
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-sfc/src/script/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class ScriptCompileContext {
hasDefineOptionsCall = false
hasDefineSlotsCall = false
hasDefineModelCall = false
hasDefineAttrsCall = false

// defineProps
propsCall: CallExpression | undefined
Expand Down
33 changes: 33 additions & 0 deletions packages/compiler-sfc/src/script/defineAttrs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { LVal, Node } from '@babel/types'
import { isCallOf } from './utils'
import { ScriptCompileContext } from './context'

export const DEFINE_ATTRS = 'defineAttrs'

export function processDefineAttrs(
ctx: ScriptCompileContext,
node: Node,
declId?: LVal
): boolean {
if (!isCallOf(node, DEFINE_ATTRS)) {
return false
}
if (ctx.hasDefineAttrsCall) {
ctx.error(`duplicate ${DEFINE_ATTRS}() call`, node)
}
ctx.hasDefineAttrsCall = true

if (node.arguments.length > 0) {
ctx.error(`${DEFINE_ATTRS}() cannot accept arguments`, node)
}

if (declId) {
ctx.s.overwrite(
ctx.startOffset! + node.start!,
ctx.startOffset! + node.end!,
`${ctx.helper('useAttrs')}()`
)
}

return true
}
12 changes: 11 additions & 1 deletion packages/runtime-core/src/apiSetupHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
ComponentOptionsMixin,
ComponentOptionsWithoutProps,
ComputedOptions,
MethodOptions
MethodOptions,
StrictUnwrapAttrsType
} from './componentOptions'
import {
ComponentPropsOptions,
Expand Down Expand Up @@ -215,6 +216,15 @@ export function defineSlots<
return null as any
}

export function defineAttrs<
Attrs extends Record<string, any> = Record<string, any>
>(): StrictUnwrapAttrsType<Attrs> {
if (__DEV__) {
warnRuntimeUsage(`defineAttrs`)
}
return null as any
}

/**
* (**Experimental**) Vue `<script setup>` compiler macro for declaring a
* two-way binding prop that can be consumed via `v-model` from the parent
Expand Down
11 changes: 11 additions & 0 deletions packages/runtime-core/src/componentOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,17 @@ export type ExtractComputedReturns<T extends any> = {
: never
}

export type StrictUnwrapAttrsType<
Attrs extends Record<string, unknown>,
T = NonNullable<Attrs>
> = [keyof T] extends [never]
? Record<string, unknown>
: T extends new () => { $props: infer P }
? Readonly<NonNullable<P>>
: T extends (props: infer P, ...args: any) => any
? Readonly<NonNullable<P>>
: Readonly<T>

export type ObjectWatchOptionItem = {
handler: WatchCallback | string
} & WatchOptions
Expand Down
1 change: 1 addition & 0 deletions packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export {
defineExpose,
defineOptions,
defineSlots,
defineAttrs,
defineModel,
withDefaults,
useModel,
Expand Down
2 changes: 2 additions & 0 deletions packages/runtime-core/types/scriptSetupHelpers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type _defineOptions = typeof defineOptions
type _defineSlots = typeof defineSlots
type _defineModel = typeof defineModel
type _withDefaults = typeof withDefaults
type _defineAttrs = typeof defineAttrs

declare global {
const defineProps: _defineProps
Expand All @@ -16,4 +17,5 @@ declare global {
const defineSlots: _defineSlots
const defineModel: _defineModel
const withDefaults: _withDefaults
const defineAttrs: _defineAttrs
}

0 comments on commit 94a60f3

Please sign in to comment.