Skip to content

Commit 90a1862

Browse files
committed
feat(api): add skipCheck for prop
closes #362
1 parent 54eb555 commit 90a1862

File tree

13 files changed

+240
-60
lines changed

13 files changed

+240
-60
lines changed

.changeset/fluffy-ads-bake.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@vue-macros/better-define': patch
3+
'@vue-macros/define-prop': patch
4+
'@vue-macros/api': patch
5+
---
6+
7+
add `skipCheck` for prop

packages/api/src/vue/utils.ts

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
resolveTSReferencedType,
77
} from '../ts'
88

9+
export const UNKNOWN_TYPE = 'Unknown'
10+
911
export async function inferRuntimeType(
1012
node: TSResolvedType | TSExports
1113
): Promise<string[]> {
@@ -51,9 +53,8 @@ export async function inferRuntimeType(
5153
case 'NumericLiteral':
5254
case 'BigIntLiteral':
5355
return ['Number']
54-
default:
55-
return [`null`]
5656
}
57+
break
5758

5859
case 'TSTypeReference':
5960
if (node.type.typeName.type === 'Identifier') {
@@ -88,7 +89,7 @@ export async function inferRuntimeType(
8889
})
8990
if (t) return inferRuntimeType(t)
9091
}
91-
return ['null']
92+
break
9293
case 'Exclude':
9394
if (
9495
node.type.typeParameters &&
@@ -100,10 +101,10 @@ export async function inferRuntimeType(
100101
})
101102
if (t) return inferRuntimeType(t)
102103
}
103-
return ['null']
104+
break
104105
}
105106
}
106-
return [`null`]
107+
break
107108

108109
case 'TSUnionType': {
109110
const types = (
@@ -129,25 +130,50 @@ export async function inferRuntimeType(
129130

130131
case 'TSInterfaceDeclaration':
131132
return ['Object']
132-
133-
default:
134-
return [`null`] // no runtime check
135133
}
134+
135+
// no runtime check
136+
return [UNKNOWN_TYPE]
136137
}
137138

138139
export function attachNodeLoc(node: Node, newNode: Node) {
139140
newNode.start = node.start
140141
newNode.end = node.end
141142
}
142143

143-
export function toRuntimeTypeString(types: string[], isProduction?: boolean) {
144-
if (isProduction) {
144+
export function genRuntimePropDefinition(
145+
types: string[] | undefined,
146+
isProduction: boolean,
147+
properties: string[]
148+
) {
149+
let type: string | undefined
150+
let skipCheck = false
151+
152+
if (types) {
145153
const hasBoolean = types.includes('Boolean')
146-
types = types.filter(
147-
(t) =>
148-
t === 'Boolean' || (hasBoolean && t === 'String') || t === 'Function'
149-
)
154+
const hasUnknown = types.includes(UNKNOWN_TYPE)
155+
156+
if (hasUnknown) types = types.filter((t) => t !== UNKNOWN_TYPE)
157+
158+
if (isProduction) {
159+
types = types.filter(
160+
(t) =>
161+
t === 'Boolean' || (hasBoolean && t === 'String') || t === 'Function'
162+
)
163+
} else if (hasUnknown) {
164+
types = types.filter((t) => t === 'Boolean' || t === 'Function')
165+
skipCheck = types.length > 0
166+
}
167+
168+
if (types.length > 0) {
169+
type = types.length > 1 ? `[${types.join(', ')}]` : types[0]
170+
}
150171
}
151-
if (types.length === 0) return undefined
152-
return types.length > 1 ? `[${types.join(', ')}]` : types[0]
172+
173+
const pairs: string[] = []
174+
if (type) pairs.push(`type: ${type}`)
175+
if (skipCheck) pairs.push(`skipCheck: true`)
176+
pairs.push(...properties)
177+
178+
return pairs.length > 0 ? `{ ${pairs.join(', ')} }` : 'null'
153179
}

packages/better-define/src/core/index.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ import {
88
type TSEmits,
99
type TSProps,
1010
analyzeSFC,
11-
toRuntimeTypeString,
11+
genRuntimePropDefinition,
1212
} from '@vue-macros/api'
1313

1414
export async function transformBetterDefine(
1515
code: string,
1616
id: string,
17-
isProduction?: boolean
17+
isProduction = false
1818
) {
1919
const s = new MagicString(code)
2020
const sfc = parseSFC(code, id)
@@ -39,18 +39,15 @@ export async function transformBetterDefine(
3939
let defaultString = ''
4040
if (defaultDecl) defaultString = defaultDecl('default')
4141
42-
const typeString = toRuntimeTypeString(type, isProduction)
43-
let def: string
44-
if (!isProduction) {
45-
def = `{ type: ${typeString}, required: ${required}, ${defaultString} }`
46-
} else if (typeString) {
47-
def = `{ type: ${typeString}, ${defaultString} }`
48-
} else {
49-
// production: checks are useless
50-
def = `${defaultString ? `{ ${defaultString} }` : 'null'}`
51-
}
42+
const properties: string[] = []
43+
if (!isProduction) properties.push(`required: ${required}`)
44+
if (defaultString) properties.push(defaultString)
5245
53-
return `${JSON.stringify(key)}: ${def}`
46+
return `${JSON.stringify(key)}: ${genRuntimePropDefinition(
47+
type,
48+
isProduction,
49+
properties
50+
)}`
5451
})
5552
.join(',\n ')}\n}`
5653

packages/better-define/tests/__snapshots__/fixtures.test.ts.snap

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
120120
} },
121121
\\"qux\\": { type: Function, required: false, default: () => {
122122
} },
123-
\\"quux\\": { type: null, required: false, default: abc }
123+
\\"quux\\": { required: false, default: abc }
124124
},
125125
setup(__props) {
126126
return (_ctx, _cache) => {
@@ -454,6 +454,74 @@ export { issue351 as default };
454454
"
455455
`;
456456

457+
exports[`fixtures > tests/fixtures/issue-362.vue > isProduction = false 1`] = `
458+
"import { defineComponent, openBlock, createElementBlock, toDisplayString } from 'vue';
459+
import _export_sfc from '/plugin-vue/export-helper';
460+
461+
var _sfc_main = /* @__PURE__ */ defineComponent({
462+
__name: \\"issue-362\\",
463+
props: {
464+
\\"trigger\\": { required: true }
465+
},
466+
setup(__props) {
467+
const props = __props;
468+
const Trigger = {
469+
click: \\"click\\",
470+
focus: \\"focus\\",
471+
hover: \\"hover\\"
472+
};
473+
return (_ctx, _cache) => {
474+
return openBlock(), createElementBlock(
475+
\\"button\\",
476+
null,
477+
toDisplayString(__props.trigger),
478+
1
479+
/* TEXT */
480+
);
481+
};
482+
}
483+
});
484+
485+
var issue362 = /* @__PURE__ */ _export_sfc(_sfc_main, [__FILE__]);
486+
487+
export { issue362 as default };
488+
"
489+
`;
490+
491+
exports[`fixtures > tests/fixtures/issue-362.vue > isProduction = true 1`] = `
492+
"import { defineComponent, openBlock, createElementBlock, toDisplayString } from 'vue';
493+
import _export_sfc from '/plugin-vue/export-helper';
494+
495+
var _sfc_main = /* @__PURE__ */ defineComponent({
496+
__name: \\"issue-362\\",
497+
props: {
498+
\\"trigger\\": null
499+
},
500+
setup(__props) {
501+
const props = __props;
502+
const Trigger = {
503+
click: \\"click\\",
504+
focus: \\"focus\\",
505+
hover: \\"hover\\"
506+
};
507+
return (_ctx, _cache) => {
508+
return openBlock(), createElementBlock(
509+
\\"button\\",
510+
null,
511+
toDisplayString(__props.trigger),
512+
1
513+
/* TEXT */
514+
);
515+
};
516+
}
517+
});
518+
519+
var issue362 = /* @__PURE__ */ _export_sfc(_sfc_main, [__FILE__]);
520+
521+
export { issue362 as default };
522+
"
523+
`;
524+
457525
exports[`fixtures > tests/fixtures/optional-method.vue > isProduction = false 1`] = `
458526
"import { defineComponent } from 'vue';
459527
import _export_sfc from '/plugin-vue/export-helper';
@@ -498,6 +566,56 @@ export { optionalMethod as default };
498566
"
499567
`;
500568

569+
exports[`fixtures > tests/fixtures/resolve-failed.vue > isProduction = false 1`] = `
570+
"import { defineComponent } from 'vue';
571+
import _export_sfc from '/plugin-vue/export-helper';
572+
573+
var _sfc_main = /* @__PURE__ */ defineComponent({
574+
__name: \\"resolve-failed\\",
575+
props: {
576+
\\"foo\\": { required: true },
577+
\\"bar\\": { type: Object, required: true },
578+
\\"bool\\": { type: Boolean, skipCheck: true, required: true },
579+
\\"fun\\": { type: Function, skipCheck: true, required: true },
580+
\\"boolAndFun\\": { type: [Boolean, Function], skipCheck: true, required: true }
581+
},
582+
setup(__props) {
583+
return () => {
584+
};
585+
}
586+
});
587+
588+
var resolveFailed = /* @__PURE__ */ _export_sfc(_sfc_main, [__FILE__]);
589+
590+
export { resolveFailed as default };
591+
"
592+
`;
593+
594+
exports[`fixtures > tests/fixtures/resolve-failed.vue > isProduction = true 1`] = `
595+
"import { defineComponent } from 'vue';
596+
import _export_sfc from '/plugin-vue/export-helper';
597+
598+
var _sfc_main = /* @__PURE__ */ defineComponent({
599+
__name: \\"resolve-failed\\",
600+
props: {
601+
\\"foo\\": null,
602+
\\"bar\\": null,
603+
\\"bool\\": { type: Boolean },
604+
\\"fun\\": { type: Function },
605+
\\"boolAndFun\\": { type: [Boolean, Function] }
606+
},
607+
setup(__props) {
608+
return () => {
609+
};
610+
}
611+
});
612+
613+
var resolveFailed = /* @__PURE__ */ _export_sfc(_sfc_main, [__FILE__]);
614+
615+
export { resolveFailed as default };
616+
"
617+
`;
618+
501619
exports[`fixtures > tests/fixtures/special-key.vue > isProduction = false 1`] = `
502620
"import { defineComponent } from 'vue';
503621
import _export_sfc from '/plugin-vue/export-helper';
@@ -723,7 +841,7 @@ import _export_sfc from '/plugin-vue/export-helper';
723841
var _sfc_main = /* @__PURE__ */ defineComponent({
724842
__name: \\"unresolved\\",
725843
props: {
726-
\\"foo\\": { type: null, required: true }
844+
\\"foo\\": { required: true }
727845
},
728846
setup(__props) {
729847
return () => {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script setup lang="ts">
2+
const Trigger = {
3+
click: 'click',
4+
focus: 'focus',
5+
hover: 'hover',
6+
} as const
7+
8+
const props = defineProps<{
9+
trigger: keyof typeof Trigger | Array<keyof typeof Trigger>
10+
}>()
11+
</script>
12+
13+
<template>
14+
<button>{{ trigger }}</button>
15+
</template>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script setup lang="ts">
2+
defineProps<{
3+
// @ts-expect-error
4+
foo: Foo
5+
bar: Pick<{ foo: string }, 'foo'>
6+
bool: unknown | boolean
7+
fun: unknown | Function
8+
boolAndFun: unknown | boolean | Function | number
9+
}>()
10+
</script>

packages/define-prop/src/core/index.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,7 @@ import {
99
parseSFC,
1010
walkAST,
1111
} from '@vue-macros/common'
12-
import {
13-
inferRuntimeType,
14-
resolveTSReferencedType,
15-
toRuntimeTypeString,
16-
} from '@vue-macros/api'
12+
import { inferRuntimeType, resolveTSReferencedType } from '@vue-macros/api'
1713
import { type Node, type TSType } from '@babel/types'
1814
import { kevinEdition } from './kevin-edition'
1915
import { johnsonEdition } from './johnson-edition'
@@ -72,7 +68,7 @@ export async function transformDefineProp(
7268
`${DEFINE_PROP} can not be used in the same file as ${DEFINE_PROPS}.`
7369
)
7470

75-
const runtimeProps = await genRuntimeProps()
71+
const runtimeProps = await genRuntimeProps(isProduction)
7672
if (runtimeProps)
7773
s.prependLeft(
7874
offset!,
@@ -90,9 +86,6 @@ export async function transformDefineProp(
9086
},
9187
type,
9288
})
93-
return (
94-
resolved &&
95-
toRuntimeTypeString(await inferRuntimeType(resolved), isProduction)
96-
)
89+
return resolved && inferRuntimeType(resolved)
9790
}
9891
}

0 commit comments

Comments
 (0)