Skip to content

feat(helper): resolve component #212

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions packages/runtime-vapor/__tests__/resolveAssets.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {
type Component,
createComponent,
createTextNode,
ref,
resolveComponent,
setText,
template,
watchEffect,
} from '../src'
import { describe, expect } from 'vitest'
import { makeRender } from './_utils'

const define = makeRender()

describe('resolveAssets', () => {
test('should work', async () => {
const child = {
name: 'FooBaz',
setup() {
return (() => {
const n2 = createTextNode()
return n2
})()
},
}
const { render } = define({
name: 'parent',
setup() {
return (() => {
createComponent(child)
expect(resolveComponent('foo-baz')).toBeDefined()
expect(resolveComponent('foo-baz')).toBeDefined()
expect(resolveComponent('FooBaz')).toBeDefined()
})()
},
})
render()
})
test('maybe self ref', async () => {
let comp: Component | null = null
const rootCompObj = {
name: 'Root',
setup() {
comp = resolveComponent('Root', true, true)
},
}
const root = define(rootCompObj)
root.render()
expect(comp).toMatchObject(rootCompObj)
})
describe('warning', () => {
test('outside render() or setup()', () => {
resolveComponent('foo')
expect(
'resolveComponent can only be used in render() or setup().',
).toHaveBeenWarned()
})
test('not exists', () => {
const root = {
setup() {
resolveComponent('not-exists-component')
return (() => {
const n = createTextNode()
})()
},
}
const { render } = define(root)
render()
expect(
'Failed to resolve component: not-exists-component',
).toHaveBeenWarned()
})
})
})
5 changes: 5 additions & 0 deletions packages/runtime-vapor/src/apiCreateComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export function createComponent(

// register sub-component with current component for lifecycle management
current.comps.add(instance)
current.components[
instance.component.name ??
instance.component.__name ??
instance.component.__file!
] = instance.component

return instance
}
2 changes: 2 additions & 0 deletions packages/runtime-vapor/src/apiCreateVaporApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export function createAppContext(): AppContext {
warnHandler: undefined,
},
provides: Object.create(null),
components: {},
}
}

Expand Down Expand Up @@ -137,6 +138,7 @@ export interface AppContext {
app: App // for devtools
config: AppConfig
provides: Record<string | symbol, any>
components: Record<string, Component>
}

/**
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-vapor/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ export type SetupContext<E = EmitsOptions> = E extends any
}
: never

export const getComponentName = (component: Component) => {
return isFunction(component)
? component.displayName || component.name
: component.name
}

export function createSetupContext(
instance: ComponentInternalInstance,
): SetupContext {
Expand Down Expand Up @@ -157,6 +163,7 @@ export interface ComponentInternalInstance {
scope: EffectScope
component: Component
comps: Set<ComponentInternalInstance>
components: Record<string, Component>
dirs: Map<Node, DirectiveBinding[]>

rawProps: NormalizedRawProps
Expand Down Expand Up @@ -283,6 +290,7 @@ export function createComponentInstance(
provides: parent ? parent.provides : Object.create(_appContext.provides),
component,
comps: new Set(),
components: {},
dirs: new Map(),

// resolved props and emits options
Expand Down
56 changes: 54 additions & 2 deletions packages/runtime-vapor/src/helpers/resolveAssets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,57 @@
export function resolveComponent() {
// TODO
import { camelize, capitalize } from '@vue/shared'
import {
type ComponentInternalInstance,
currentInstance,
getComponentName,
} from '../component'
import { warn } from '../warning'

const resolve = (registry: Record<string, any> | undefined, name: string) => {
return (
registry &&
(registry[name] ||
registry[camelize(name)] ||
registry[capitalize(camelize(name))])
)
}

export function resolveComponent(
name: string,
warnMissing = true,
maybeSelfReference = false,
) {
const instance = currentInstance
if (!instance?.component) {
if (__DEV__) {
warn(`resolveComponent ` + `can only be used in render() or setup().`)
}
return
}
const component = instance.component
const selfName = getComponentName(component)
if (
selfName &&
(selfName === name ||
selfName === camelize(name) ||
selfName === capitalize(camelize(name)))
) {
return component
}
const registry =
instance.components ?? (component as ComponentInternalInstance).component
const res =
resolve(registry, name) || resolve(instance.appContext.components, name)
if (!res && maybeSelfReference) {
return component
}
if (__DEV__ && warnMissing && !res) {
const extra =
`\nIf this is a native custom element, make sure to exclude it from ` +
`component resolution via compilerOptions.isCustomElement.`
warn(`Failed to resolve component: ${name}${extra}`)
}
return res
// // TODO
}

export function resolveDirective() {
Expand Down