Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update #169

Merged
merged 9 commits into from
Oct 15, 2024
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"version": "3.5.12",
"packageManager": "pnpm@9.12.0",
"packageManager": "pnpm@9.12.1",
"type": "module",
"scripts": {
"dev": "node scripts/dev.js",
Expand Down Expand Up @@ -66,9 +66,9 @@
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-replace": "5.0.4",
"@swc/core": "^1.7.28",
"@swc/core": "^1.7.35",
"@types/hash-sum": "^1.0.2",
"@types/node": "^20.16.10",
"@types/node": "^20.16.11",
"@types/semver": "^7.5.8",
"@types/serve-handler": "^6.1.4",
"@vitest/coverage-v8": "^2.1.1",
Expand All @@ -84,7 +84,7 @@
"jsdom": "^25.0.0",
"lint-staged": "^15.2.10",
"lodash": "^4.17.21",
"magic-string": "^0.30.11",
"magic-string": "^0.30.12",
"markdown-table": "^3.0.3",
"marked": "13.0.3",
"npm-run-all2": "^6.2.3",
Expand All @@ -105,7 +105,7 @@
"todomvc-app-css": "^2.4.3",
"tslib": "^2.7.0",
"typescript": "~5.6.2",
"typescript-eslint": "^8.8.0",
"typescript-eslint": "^8.8.1",
"vite": "catalog:",
"vitest": "^2.1.1"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,33 @@ export default /*@__PURE__*/_defineComponent({



return { }
}

})"
`;

exports[`defineProps > w/ extends intersection type 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
type Foo = {
x?: number;
};
interface Props extends Foo {
z: number
y: string
}

export default /*@__PURE__*/_defineComponent({
props: {
z: { type: Number, required: true },
y: { type: String, required: true },
x: { type: Number, required: false }
},
setup(__props: any, { expose: __expose }) {
__expose();



return { }
}

Expand Down Expand Up @@ -268,6 +295,31 @@ export default /*@__PURE__*/_defineComponent({



return { }
}

})"
`;

exports[`defineProps > w/ intersection type 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
type Foo = {
x?: number;
};
type Bar = {
y: string;
};

export default /*@__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false },
y: { type: String, required: true }
},
setup(__props: any, { expose: __expose }) {
__expose();



return { }
}

Expand Down
45 changes: 45 additions & 0 deletions packages/compiler-sfc/__tests__/compileScript/defineProps.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,51 @@ const props = defineProps({ foo: String })
})
})

test('w/ extends intersection type', () => {
const { content, bindings } = compile(`
<script setup lang="ts">
type Foo = {
x?: number;
};
interface Props extends Foo {
z: number
y: string
}
defineProps<Props>()
</script>
`)
assertCode(content)
expect(content).toMatch(`z: { type: Number, required: true }`)
expect(content).toMatch(`y: { type: String, required: true }`)
expect(content).toMatch(`x: { type: Number, required: false }`)
expect(bindings).toStrictEqual({
x: BindingTypes.PROPS,
y: BindingTypes.PROPS,
z: BindingTypes.PROPS,
})
})

test('w/ intersection type', () => {
const { content, bindings } = compile(`
<script setup lang="ts">
type Foo = {
x?: number;
};
type Bar = {
y: string;
};
defineProps<Foo & Bar>()
</script>
`)
assertCode(content)
expect(content).toMatch(`y: { type: String, required: true }`)
expect(content).toMatch(`x: { type: Number, required: false }`)
expect(bindings).toStrictEqual({
x: BindingTypes.PROPS,
y: BindingTypes.PROPS,
})
})

test('w/ exported interface', () => {
const { content, bindings } = compile(`
<script setup lang="ts">
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler-sfc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@
"postcss-modules": "^6.0.0",
"postcss-selector-parser": "^6.1.2",
"pug": "^3.0.3",
"sass": "^1.79.4"
"sass": "^1.79.5"
}
}
21 changes: 21 additions & 0 deletions packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import {
Fragment,
type FunctionalComponent,
Teleport,
createBlock,
createCommentVNode,
createElementBlock,
Expand Down Expand Up @@ -391,6 +392,26 @@ describe('attribute fallthrough', () => {
expect(`Extraneous non-emits event listeners`).toHaveBeenWarned()
})

it('should warn when fallthrough fails on teleport root node', () => {
const Parent = {
render() {
return h(Child, { class: 'parent' })
},
}
const root = document.createElement('div')

const Child = defineComponent({
render() {
return h(Teleport, { to: root }, h('div'))
},
})

document.body.appendChild(root)
render(h(Parent), root)

expect(`Extraneous non-props attributes (class)`).toHaveBeenWarned()
})

it('should dedupe same listeners when $attrs is used during render', () => {
const click = vi.fn()
const count = ref(0)
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1006,7 +1006,7 @@ export function finishComponentSetup(
instance.vnode.props &&
instance.vnode.props['inline-template']) ||
Component.template ||
resolveMergedOptions(instance).template
(__FEATURE_OPTIONS_API__ && resolveMergedOptions(instance).template)
if (template) {
if (__DEV__) {
startMeasure(instance, `compile`)
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-core/src/componentRenderUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export function renderComponentRoot(
`Extraneous non-props attributes (` +
`${extraAttrs.join(', ')}) ` +
`were passed to component but could not be automatically inherited ` +
`because component renders fragment or text root nodes.`,
`because component renders fragment or text or teleport root nodes.`,
)
}
if (eventAttrs.length) {
Expand Down
142 changes: 141 additions & 1 deletion packages/shared/__tests__/normalizeProp.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { normalizeClass, parseStringStyle } from '../src'
import {
normalizeClass,
normalizeProps,
normalizeStyle,
parseStringStyle,
stringifyStyle,
} from '../src'

describe('normalizeClass', () => {
test('handles undefined correctly', () => {
Expand All @@ -15,6 +21,11 @@ describe('normalizeClass', () => {
)
})

test('handles string containing spaces correctly', () => {
expect(normalizeClass('foo1 ')).toEqual('foo1')
expect(normalizeClass(['foo ', ' baz '])).toEqual('foo baz')
})

test('handles empty array correctly', () => {
expect(normalizeClass([])).toEqual('')
})
Expand Down Expand Up @@ -92,3 +103,132 @@ describe('normalizeClass', () => {
`)
})
})

describe('normalizeStyle', () => {
test('handles string correctly', () => {
expect(normalizeStyle('foo')).toEqual('foo')
})

test('handles array correctly', () => {
const style: any = normalizeStyle([
`border: 1px solid transparent;
background: linear-gradient(white, white) padding-box,
repeating-linear-gradient(
-45deg,
#ccc 0,
#ccc 0.5em,
white 0,
white 0.75em
);`,
])

expect(style.border).toEqual('1px solid transparent')

expect(style.background).toEqual(`linear-gradient(white, white) padding-box,
repeating-linear-gradient(
-45deg,
#ccc 0,
#ccc 0.5em,
white 0,
white 0.75em
)`)
})

test('handles object correctly', () => {
const styleObj = {
border: '1px solid transparent',
background: `linear-gradient(white, white) padding-box,
repeating-linear-gradient(
-45deg,
#ccc 0,
#ccc 0.5em,
white 0,
white 0.75em
)`,
}
const style: any = normalizeStyle(styleObj)
expect(style.border).toEqual(styleObj.border)
expect(style.background).toEqual(styleObj.background)
})
})

describe('stringifyStyle', () => {
test('should return empty string for undefined or string styles', () => {
expect(stringifyStyle(undefined)).toBe('')
expect(stringifyStyle('')).toBe('')
expect(stringifyStyle('color: blue;')).toBe('')
})

test('should return valid CSS string for normalized style object', () => {
const style = {
color: 'blue',
fontSize: '14px',
backgroundColor: 'white',
opacity: 0.8,
margin: 0,
'--custom-color': 'red',
}

expect(stringifyStyle(style)).toBe(
'color:blue;font-size:14px;background-color:white;opacity:0.8;margin:0;--custom-color:red;',
)
})

test('should ignore non-string or non-number values in style object', () => {
const style: any = {
color: 'blue',
fontSize: '14px',
lineHeight: true,
padding: null,
margin: undefined,
}

const expected = 'color:blue;font-size:14px;'
expect(stringifyStyle(style)).toBe(expected)
})
})

describe('normalizeProps', () => {
test('should return null when props is null', () => {
const props = null
const result = normalizeProps(props)
expect(result).toBeNull()
})

test('should normalize class prop when it is an array', () => {
const props = {
class: ['class1', 'class2'],
}
const result = normalizeProps(props)
expect(result).toEqual({
class: 'class1 class2',
})
})

test('should normalize class prop when it is an object', () => {
const props = {
class: {
class1: true,
class2: false,
class3: true,
},
}
const result = normalizeProps(props)
expect(result).toEqual({
class: 'class1 class3',
})
})

test('should normalize style prop', () => {
const props = {
style: ['color: blue', 'font-size: 14px'],
}
const result = normalizeProps(props)
expect(result).toEqual({
style: {
color: 'blue',
'font-size': '14px',
},
})
})
})
Loading