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

fix(runtime-core): Avoid mutating original options object in createApp (fix #4398) #4840

Merged
merged 4 commits into from Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts
Expand Up @@ -91,9 +91,9 @@ describe('resolveAssets', () => {
const root = nodeOps.createElement('div')
app.mount(root)

expect(component1!).toBe(Root) // explicit self name reference
expect(component1!).toMatchObject(Root) // explicit self name reference
expect(component2!).toBe(Foo) // successful resolve take higher priority
expect(component3!).toBe(Root) // fallback when resolve fails
expect(component3!).toMatchObject(Root) // fallback when resolve fails
})

describe('warning', () => {
Expand Down
5 changes: 5 additions & 0 deletions packages/runtime-core/src/apiCreateApp.ts
Expand Up @@ -179,6 +179,11 @@ export function createAppAPI<HostElement>(
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
return function createApp(rootComponent, rootProps = null) {

if (!isFunction(rootComponent)) {
rootComponent = { ...rootComponent }
}

if (rootProps != null && !isObject(rootProps)) {
__DEV__ && warn(`root props passed to app.mount() must be an object.`)
rootProps = null
Expand Down
29 changes: 29 additions & 0 deletions packages/runtime-dom/__tests__/createApp.spec.ts
Expand Up @@ -12,4 +12,33 @@ describe('createApp for dom', () => {
expect(root.children.length).toBe(1)
expect(root.children[0] instanceof SVGElement).toBe(true)
})

// #4398
test('should not mutate original root component options object', () => {

const originalObj = {
data() {
return {
counter: 0
}
}
}

const handler = jest.fn(msg => {
expect(msg).toMatch(`Component is missing template or render function`)
})

const Root = { ...originalObj}

const app = createApp(Root)
app.config.warnHandler = handler
app.mount(document.createElement('div'))

// ensure mount is based on a copy of Root object rather than Root object itself
expect(app._component).not.toBe(Root)

// ensure no mutation happened to Root object
expect(originalObj).toMatchObject(Root)

})
})