Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions graph/test/typescript/cases/80-vue-definecomponent-jsx/lib/vue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// The Vue entry points that matter, declared with their shapes. No Vue dependency.
declare global {
namespace JSX {
type Element = string;
interface ElementAttributesProperty {
props: unknown;
}
interface IntrinsicElements {
[name: string]: unknown;
}
}
}

export interface ComponentOptions {
name?: string;
props?: Record<string, unknown>;
setup?: (props: any) => unknown;
render?: () => string;
}
// A constructor type, as Vue's own DefineComponent is: nothing here says what renders.
export interface Component {
new (...args: any[]): { props: any };
}
export declare function defineComponent(options: ComponentOptions): Component;
export declare function defineComponent(setup: (props: any) => () => string): Component;
export declare function defineNuxtComponent(options: ComponentOptions): Component;

// NOT a component definer: it takes an options-shaped object and returns something else.
export declare function makeStore(options: ComponentOptions): Component;
26 changes: 26 additions & 0 deletions graph/test/typescript/cases/80-vue-definecomponent-jsx/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { defineComponent } from "../lib/vue";
import Card from "./card";
import { Child, Panel, FnForm, NuxtCard, Store } from "./parts";
import { Button, Local } from "./reg";

function helper(): number {
return 42;
}

export const App = defineComponent({
setup() {
const onPing = () => helper();
return () => (
<div>
<Card />
<Child msg="x" onPing={onPing} />
<Panel />
<FnForm />
<NuxtCard />
<Store />
<Button />
<Local />
</div>
);
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineComponent } from "../lib/vue";

export function leafButton(): number {
return 8;
}

export default defineComponent({
name: "Button",
setup() {
return () => <b>{leafButton()}</b>;
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineComponent } from "../lib/vue";
import { Child } from "./parts";

export function leafCard(): number {
return 7;
}

// the module's default export, rendered through a default import
export default defineComponent({
name: "Card",
render() {
leafCard();
return <Child msg="in card" />;
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { defineComponent, defineNuxtComponent, makeStore } from "../lib/vue";

export function leafSetup(): number {
return 1;
}
export function leafRendered(): number {
return 2;
}
export function leafRender(): number {
return 3;
}
export function leafFn(): number {
return 4;
}
export function leafNuxt(): number {
return 5;
}
export function leafStore(): number {
return 6;
}

// setup() returns the render function
export const Child = defineComponent({
props: { msg: String },
setup(props) {
leafSetup();
return () => String(leafRendered());
},
});

// the render option, no setup
export const Panel = defineComponent({
name: "Panel",
render() {
return String(leafRender());
},
});

// the function form: the function IS setup
export const FnForm = defineComponent((props) => () => String(leafFn()));

export const NuxtCard = defineNuxtComponent({
setup() {
return () => String(leafNuxt());
},
});

// CONTROL: the same object handed to another function is not a component
export const Store = makeStore({
setup() {
return () => String(leafStore());
},
});
24 changes: 24 additions & 0 deletions graph/test/typescript/cases/80-vue-definecomponent-jsx/src/reg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { defineComponent } from "../lib/vue";
import _Button from "./button";

export function leafLocal(): number {
return 9;
}

// the registration idiom: a component library re-exports each component through an
// installer that returns its argument
export function withInstall<T>(component: T): T {
return component;
}

// …of a component default-imported from its own module
export const Button = withInstall(_Button);

// …and of one defined in this module
const _Local = defineComponent({
setup() {
leafLocal();
return () => "local";
},
});
export const Local = withInstall(_Local);
36 changes: 36 additions & 0 deletions graph/test/typescript/expected/80-vue-definecomponent-jsx.edges
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ambiguous_unknown FUNCTION_CALL app#<module-init>() @L10 -> -
ambiguous_unknown FUNCTION_CALL button#<module-init>() @L7 -> -
ambiguous_unknown FUNCTION_CALL card#<module-init>() @L9 -> -
ambiguous_unknown FUNCTION_CALL parts#<arrow@27>() @L27 -> -
ambiguous_unknown FUNCTION_CALL parts#<arrow@40>() @L40 -> -
ambiguous_unknown FUNCTION_CALL parts#<arrow@44>() @L44 -> -
ambiguous_unknown FUNCTION_CALL parts#<arrow@51>() @L51 -> -
ambiguous_unknown FUNCTION_CALL parts#<module-init>() @L23 -> -
ambiguous_unknown FUNCTION_CALL parts#<module-init>() @L32 -> -
ambiguous_unknown FUNCTION_CALL parts#<module-init>() @L40 -> -
ambiguous_unknown FUNCTION_CALL parts#<module-init>() @L42 -> -
ambiguous_unknown FUNCTION_CALL parts#<module-init>() @L49 -> -
ambiguous_unknown FUNCTION_CALL parts#render() @L35 -> -
ambiguous_unknown FUNCTION_CALL reg#<module-init>() @L18 -> -
ambiguous_unknown JSX_COMPONENT_CALL app#<arrow@13>() @L20 -> -
callback_registered FUNCTION_CALL parts#<module-init>() @L40 -> parts#<arrow@40>(?)
known_edge FUNCTION_CALL app#onPing() @L12 -> app#helper()
known_edge FUNCTION_CALL button#<arrow@10>() @L10 -> button#leafButton()
known_edge FUNCTION_CALL card#render() @L12 -> card#leafCard()
known_edge FUNCTION_CALL parts#<arrow@27>() @L27 -> parts#leafRendered()
known_edge FUNCTION_CALL parts#<arrow@40>() @L40 -> parts#leafFn()
known_edge FUNCTION_CALL parts#<arrow@44>() @L44 -> parts#leafNuxt()
known_edge FUNCTION_CALL parts#<arrow@51>() @L51 -> parts#leafStore()
known_edge FUNCTION_CALL parts#render() @L35 -> parts#leafRender()
known_edge FUNCTION_CALL parts#setup(?) @L26 -> parts#leafSetup()
known_edge FUNCTION_CALL reg#<module-init>() @L15 -> reg#withInstall(T)
known_edge FUNCTION_CALL reg#<module-init>() @L24 -> reg#withInstall(T)
known_edge FUNCTION_CALL reg#setup() @L20 -> reg#leafLocal()
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L15 -> card#render()
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L16 -> parts#setup(?)
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L17 -> parts#render()
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L18 -> parts#<arrow@40>(?)
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L19 -> parts#setup()@L43
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L21 -> button#setup()
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L22 -> reg#setup()
known_edge JSX_COMPONENT_CALL card#render() @L13 -> parts#setup(?)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
── entry_point (1) ──
unimported_module app#<module> app.tsx:1
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
80-vue-definecomponent-jsx [fields]
precision 0.0000 (0 correct, 0 wrong)
recall 0.0000 (0 of 0 the compiler resolved)
sites 0 resolved 0
tiers
access
not scored: 0 rows whose target is not a client declaration
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ambiguous_unknown FUNCTION_CALL parts#<arrow@27>() @L27 -> -
ambiguous_unknown FUNCTION_CALL parts#<arrow@40>() @L40 -> -
ambiguous_unknown FUNCTION_CALL parts#<arrow@44>() @L44 -> -
ambiguous_unknown FUNCTION_CALL parts#<arrow@51>() @L51 -> -
ambiguous_unknown FUNCTION_CALL parts#render() @L35 -> -
ambiguous_unknown JSX_COMPONENT_CALL app#<arrow@13>() @L20 -> -
boundary_lib FUNCTION_CALL app#<module-init>() @L10 -> vue#defineComponent(ComponentOptions)
boundary_lib FUNCTION_CALL button#<module-init>() @L7 -> vue#defineComponent(ComponentOptions)
boundary_lib FUNCTION_CALL card#<module-init>() @L9 -> vue#defineComponent(ComponentOptions)
boundary_lib FUNCTION_CALL parts#<module-init>() @L23 -> vue#defineComponent(ComponentOptions)
boundary_lib FUNCTION_CALL parts#<module-init>() @L32 -> vue#defineComponent(ComponentOptions)
boundary_lib FUNCTION_CALL parts#<module-init>() @L40 -> vue#defineComponent((props: any) =)
boundary_lib FUNCTION_CALL parts#<module-init>() @L42 -> vue#defineNuxtComponent(ComponentOptions)
boundary_lib FUNCTION_CALL parts#<module-init>() @L49 -> vue#makeStore(ComponentOptions)
boundary_lib FUNCTION_CALL reg#<module-init>() @L18 -> vue#defineComponent(ComponentOptions)
callback_registered FUNCTION_CALL parts#<module-init>() @L40 -> parts#<arrow@40>(?)
known_edge FUNCTION_CALL app#onPing() @L12 -> app#helper()
known_edge FUNCTION_CALL button#<arrow@10>() @L10 -> button#leafButton()
known_edge FUNCTION_CALL card#render() @L12 -> card#leafCard()
known_edge FUNCTION_CALL parts#<arrow@27>() @L27 -> parts#leafRendered()
known_edge FUNCTION_CALL parts#<arrow@40>() @L40 -> parts#leafFn()
known_edge FUNCTION_CALL parts#<arrow@44>() @L44 -> parts#leafNuxt()
known_edge FUNCTION_CALL parts#<arrow@51>() @L51 -> parts#leafStore()
known_edge FUNCTION_CALL parts#render() @L35 -> parts#leafRender()
known_edge FUNCTION_CALL parts#setup(?) @L26 -> parts#leafSetup()
known_edge FUNCTION_CALL reg#<module-init>() @L15 -> reg#withInstall(T)
known_edge FUNCTION_CALL reg#<module-init>() @L24 -> reg#withInstall(T)
known_edge FUNCTION_CALL reg#setup() @L20 -> reg#leafLocal()
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L15 -> card#render()
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L16 -> parts#setup(?)
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L17 -> parts#render()
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L18 -> parts#<arrow@40>(?)
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L19 -> parts#setup()@L43
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L21 -> button#setup()
known_edge JSX_COMPONENT_CALL app#<arrow@13>() @L22 -> reg#setup()
known_edge JSX_COMPONENT_CALL card#render() @L13 -> parts#setup(?)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Accepted gaps for 80-vue-definecomponent-jsx — each line is an edge the TypeScript compiler
# resolves and this engine does not. A NEW missing edge fails the suite;
# a line here that STARTS working also fails, so the debt cannot rot.
#
# The compiler binds a Vue tag to the library's component CONSTRUCT signature, which says
# nothing about what renders; the engine binds it to the setup / render the options object
# defines instead. Both were missing before that rule too.
app#<arrow@13>() -> Component#<arrow@22>(any[])
card#render() -> Component#<arrow@22>(any[])
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
oracle=21 engine=27 agree=19 missing=2 (known 2, NEW 0) extra=8
known app#<arrow@13>() -> Component#<arrow@22>(any[])
known card#render() -> Component#<arrow@22>(any[])
extra app#<arrow@13>() -> button#setup()
extra app#<arrow@13>() -> card#render()
extra app#<arrow@13>() -> parts#<arrow@40>(?)
extra app#<arrow@13>() -> parts#render()
extra app#<arrow@13>() -> parts#setup()@L43
extra app#<arrow@13>() -> parts#setup(?)
extra app#<arrow@13>() -> reg#setup()
extra card#render() -> parts#setup(?)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
oracle=11 engine=19 agree=11 missing=0 (known 0, NEW 0) extra=8
extra app#<arrow@13>() -> button#setup()
extra app#<arrow@13>() -> card#render()
extra app#<arrow@13>() -> parts#<arrow@40>(?)
extra app#<arrow@13>() -> parts#render()
extra app#<arrow@13>() -> parts#setup()@L43
extra app#<arrow@13>() -> parts#setup(?)
extra app#<arrow@13>() -> reg#setup()
extra card#render() -> parts#setup(?)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
80-vue-definecomponent-jsx [types]
precision 0.0000 (0 correct, 0 wrong)
recall 0.0000 (0 of 0 the compiler resolved)
sites 0 resolved 0
tiers
contexts
not scored: 0 rows whose target is not a client declaration
41 changes: 41 additions & 0 deletions graph/typescript/engine/expression-resolution/callee-resolution.dl
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,47 @@ jsx_wrapper_renders(w, m) :- jsx_component_wrapper(w),
expr_child("client", w, "ARGUMENT", "0", a),
jsx_component_wrapper(a),
jsx_wrapper_renders(a, m).
// …or, when that argument NAMES a component defined elsewhere, what that one renders: the
// registration idiom `import _Button from "./Button"; export const Button =
// withInstall(_Button)`, where Button.tsx is `export default defineComponent({…})`, and
// its local and named-import forms.
jsx_wrapper_renders(w, m) :- jsx_component_wrapper(w),
expr_child("client", w, "ARGUMENT", "0", a),
expr_referenced("client", "VARIABLE", v, a),
jsx_wrapped_component(v, m).
jsx_wrapper_renders(w, m) :- jsx_component_wrapper(w),
expr_child("client", w, "ARGUMENT", "0", a),
expr_referenced("client", "IMPORT_BINDING", ih, a),
import_binds(ih, _, "VARIABLE", v),
jsx_wrapped_component(v, m).
jsx_wrapper_renders(w, m) :- jsx_component_wrapper(w),
expr_child("client", w, "ARGUMENT", "0", a),
expr_referenced("client", "IMPORT_BINDING", ih, a),
import_binding("client", "DEFAULT", _, _, _, ih),
import_target_module(ih, "client", mod2),
jsx_default_export_renders(mod2, m).
// A Vue component, `defineComponent({ setup() {…} })` / `defineComponent({ render() {…} })`
// (Nuxt: `defineNuxtComponent`). What the tag invokes with its props is the options
// object's `setup`, or its `render` option when there is no setup; a render function setup
// RETURNS is reached from setup's body, as memo's argument reaches what it defines. One
// target, not both: the site's candidates are an overload set to arity selection, and
// `setup(props)` beside a zero-parameter render arrow would lose one of them. The function
// form, `defineComponent((props) => () => …)`, is argument 0 itself, as for memo. Named by
// the definer, so the same object handed to any other call (`makeStore({ setup() {…} })`)
// stays out.
jsx_vue_definer_name("defineComponent").
jsx_vue_definer_name("defineNuxtComponent").
jsx_vue_definer(w) :- call_site("client", _, n, _, _, w, _),
jsx_vue_definer_name(n).
jsx_component_wrapper(w) :- jsx_vue_definer(w).
jsx_vue_has_setup(a) :- objlit_member_fn(a, "setup", _).
jsx_wrapper_renders(w, m) :- jsx_vue_definer(w),
expr_child("client", w, "ARGUMENT", "0", a),
objlit_member_fn(a, "setup", m).
jsx_wrapper_renders(w, m) :- jsx_vue_definer(w),
expr_child("client", w, "ARGUMENT", "0", a),
!jsx_vue_has_setup(a),
objlit_member_fn(a, "render", m).
// A wrapper call that IS the module's default export, `export default memo(Card)`,
// binds no variable: the tag reaches it through a default import of that module.
jsx_default_export_renders(mod, m) :- export_decl("client", "default", _, "DEFAULT_EXPRESSION", mod, h),
Expand Down
3 changes: 3 additions & 0 deletions graph/typescript/souffle/decls_all.dl
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@
.decl jsx_wrapped_component(c0:symbol,c1:symbol)
.decl jsx_wrapper_name(c0:symbol)
.decl jsx_wrapper_renders(c0:symbol,c1:symbol)
.decl jsx_vue_definer_name(c0:symbol)
.decl jsx_vue_definer(c0:symbol)
.decl jsx_vue_has_setup(c0:symbol)
.decl lib_boundary_reason(c0:symbol,c1:symbol)
.decl lib_capped()
.decl lib_export_relative_base(c0:symbol,c1:symbol)
Expand Down
Loading