Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 94a60f3

Browse files
committedOct 17, 2023
feat(compiler-sfc): support defineAttrs macro
1 parent 2ffe3d5 commit 94a60f3

File tree

9 files changed

+150
-3
lines changed

9 files changed

+150
-3
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`defineAttrs() > basic usage 1`] = `
4+
"import { useAttrs as _useAttrs, defineComponent as _defineComponent } from 'vue'
5+
6+
export default /*#__PURE__*/_defineComponent({
7+
setup(__props, { expose: __expose }) {
8+
__expose();
9+
10+
const attrs = _useAttrs()
11+
12+
return { attrs }
13+
}
14+
15+
})"
16+
`;
17+
18+
exports[`defineAttrs() > w/o generic params 1`] = `
19+
"import { useAttrs as _useAttrs } from 'vue'
20+
21+
export default {
22+
setup(__props, { expose: __expose }) {
23+
__expose();
24+
25+
const attrs = _useAttrs()
26+
27+
return { attrs }
28+
}
29+
30+
}"
31+
`;
32+
33+
exports[`defineAttrs() > w/o return value 1`] = `
34+
"import { defineComponent as _defineComponent } from 'vue'
35+
36+
export default /*#__PURE__*/_defineComponent({
37+
setup(__props, { expose: __expose }) {
38+
__expose();
39+
40+
41+
42+
return { }
43+
}
44+
45+
})"
46+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { compileSFCScript as compile, assertCode } from '../utils'
2+
3+
describe('defineAttrs()', () => {
4+
test('basic usage', () => {
5+
const { content } = compile(`
6+
<script setup lang="ts">
7+
const attrs = defineAttrs<{
8+
bar?: number
9+
}>()
10+
</script>
11+
`)
12+
assertCode(content)
13+
expect(content).toMatch(`const attrs = _useAttrs()`)
14+
expect(content).not.toMatch('defineAttrs')
15+
})
16+
17+
test('w/o return value', () => {
18+
const { content } = compile(`
19+
<script setup lang="ts">
20+
defineAttrs<{
21+
bar?: number
22+
}>()
23+
</script>
24+
`)
25+
assertCode(content)
26+
expect(content).not.toMatch('defineAttrs')
27+
expect(content).not.toMatch(`_useAttrs`)
28+
})
29+
30+
test('w/o generic params', () => {
31+
const { content } = compile(`
32+
<script setup>
33+
const attrs = defineAttrs()
34+
</script>
35+
`)
36+
assertCode(content)
37+
expect(content).toMatch(`const attrs = _useAttrs()`)
38+
expect(content).not.toMatch('defineAttrs')
39+
})
40+
})

‎packages/compiler-sfc/src/compileScript.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
import { analyzeScriptBindings } from './script/analyzeScriptBindings'
5454
import { isImportUsed } from './script/importUsageCheck'
5555
import { processAwait } from './script/topLevelAwait'
56+
import { processDefineAttrs } from './script/defineAttrs'
5657

5758
export interface SFCScriptCompileOptions {
5859
/**
@@ -512,7 +513,8 @@ export function compileScript(
512513
processDefineProps(ctx, expr) ||
513514
processDefineEmits(ctx, expr) ||
514515
processDefineOptions(ctx, expr) ||
515-
processDefineSlots(ctx, expr)
516+
processDefineSlots(ctx, expr) ||
517+
processDefineAttrs(ctx, expr)
516518
) {
517519
ctx.s.remove(node.start! + startOffset, node.end! + startOffset)
518520
} else if (processDefineExpose(ctx, expr)) {
@@ -550,7 +552,8 @@ export function compileScript(
550552
!isDefineProps && processDefineEmits(ctx, init, decl.id)
551553
!isDefineEmits &&
552554
(processDefineSlots(ctx, init, decl.id) ||
553-
processDefineModel(ctx, init, decl.id))
555+
processDefineModel(ctx, init, decl.id) ||
556+
processDefineAttrs(ctx, init, decl.id))
554557

555558
if (
556559
isDefineProps &&

‎packages/compiler-sfc/src/script/context.ts

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export class ScriptCompileContext {
3636
hasDefineOptionsCall = false
3737
hasDefineSlotsCall = false
3838
hasDefineModelCall = false
39+
hasDefineAttrsCall = false
3940

4041
// defineProps
4142
propsCall: CallExpression | undefined
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { LVal, Node } from '@babel/types'
2+
import { isCallOf } from './utils'
3+
import { ScriptCompileContext } from './context'
4+
5+
export const DEFINE_ATTRS = 'defineAttrs'
6+
7+
export function processDefineAttrs(
8+
ctx: ScriptCompileContext,
9+
node: Node,
10+
declId?: LVal
11+
): boolean {
12+
if (!isCallOf(node, DEFINE_ATTRS)) {
13+
return false
14+
}
15+
if (ctx.hasDefineAttrsCall) {
16+
ctx.error(`duplicate ${DEFINE_ATTRS}() call`, node)
17+
}
18+
ctx.hasDefineAttrsCall = true
19+
20+
if (node.arguments.length > 0) {
21+
ctx.error(`${DEFINE_ATTRS}() cannot accept arguments`, node)
22+
}
23+
24+
if (declId) {
25+
ctx.s.overwrite(
26+
ctx.startOffset! + node.start!,
27+
ctx.startOffset! + node.end!,
28+
`${ctx.helper('useAttrs')}()`
29+
)
30+
}
31+
32+
return true
33+
}

‎packages/runtime-core/src/apiSetupHelpers.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import {
1818
ComponentOptionsMixin,
1919
ComponentOptionsWithoutProps,
2020
ComputedOptions,
21-
MethodOptions
21+
MethodOptions,
22+
StrictUnwrapAttrsType
2223
} from './componentOptions'
2324
import {
2425
ComponentPropsOptions,
@@ -215,6 +216,15 @@ export function defineSlots<
215216
return null as any
216217
}
217218

219+
export function defineAttrs<
220+
Attrs extends Record<string, any> = Record<string, any>
221+
>(): StrictUnwrapAttrsType<Attrs> {
222+
if (__DEV__) {
223+
warnRuntimeUsage(`defineAttrs`)
224+
}
225+
return null as any
226+
}
227+
218228
/**
219229
* (**Experimental**) Vue `<script setup>` compiler macro for declaring a
220230
* two-way binding prop that can be consumed via `v-model` from the parent

‎packages/runtime-core/src/componentOptions.ts

+11
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,17 @@ export type ExtractComputedReturns<T extends any> = {
423423
: never
424424
}
425425

426+
export type StrictUnwrapAttrsType<
427+
Attrs extends Record<string, unknown>,
428+
T = NonNullable<Attrs>
429+
> = [keyof T] extends [never]
430+
? Record<string, unknown>
431+
: T extends new () => { $props: infer P }
432+
? Readonly<NonNullable<P>>
433+
: T extends (props: infer P, ...args: any) => any
434+
? Readonly<NonNullable<P>>
435+
: Readonly<T>
436+
426437
export type ObjectWatchOptionItem = {
427438
handler: WatchCallback | string
428439
} & WatchOptions

‎packages/runtime-core/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export {
7171
defineExpose,
7272
defineOptions,
7373
defineSlots,
74+
defineAttrs,
7475
defineModel,
7576
withDefaults,
7677
useModel,

‎packages/runtime-core/types/scriptSetupHelpers.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type _defineOptions = typeof defineOptions
77
type _defineSlots = typeof defineSlots
88
type _defineModel = typeof defineModel
99
type _withDefaults = typeof withDefaults
10+
type _defineAttrs = typeof defineAttrs
1011

1112
declare global {
1213
const defineProps: _defineProps
@@ -16,4 +17,5 @@ declare global {
1617
const defineSlots: _defineSlots
1718
const defineModel: _defineModel
1819
const withDefaults: _withDefaults
20+
const defineAttrs: _defineAttrs
1921
}

0 commit comments

Comments
 (0)
Please sign in to comment.